home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Amiga Plus Special 23
/
AMIGAplus Sonderheft 23 (2000)(Falke)(DE)[!].iso
/
Updates
/
AddOns
/
WormWars
/
Source
/
engine.c
< prev
next >
Wrap
C/C++ Source or Header
|
1999-11-08
|
109KB
|
3,371 lines
/* $Filename: WormWars/Source/engine.c(pp) $
* $VER: WormWars 4.1 (6.6.99) $
* $Description: Common game engine components $
*
* © Copyright 1999 James R. Jacobs.
* Freely distributable.
*
* Prerelease version.
*/
#include "stdafx.h"
#include "diff.h"
#include "same.h"
#include "engine.h"
// PRIVATE STRUCTURES -----------------------------------------------------
struct
{ SBYTE x, y, oldx, oldy;
BOOL alive, moved, teleported, visible;
} bullet[POWERLIMIT + 1];
struct
{ SBYTE x, y, deltax, deltay, last;
BOOL alive, moved;
} frag[ORBS + 2][8];
struct
{ SBYTE x, y, last;
BOOL alive;
} killer[KILLERS + 1];
struct
{ SBYTE x, y, deltax, deltay;
BOOL alive, moved;
ULONG receipt;
} missile[4];
struct
{ WORD frequency;
LONG score;
} object[LASTOBJECT + 1] =
{ {1280, 60}, // AFFIXER
{ 80, 20}, // AMMO
{ 110, 20}, // ARMOUR
{ 240, 50}, // BIAS
{ 200, 30}, // BOMB
{ 80, 10}, // BONUS
{ 360, 50}, // GROWER
{1360, 60}, // ICE
{ 150, 50}, // LIFE
{ 240, 40}, // MISSILE
{ 640, 50}, // MULTIPLIER
{ 400, 10}, // NITRO
{ 240, 30}, // POWER
{ 480, 50}, // PROTECTOR
{ 600, 40}, // SLAYER
{ 730, 70}, // SWITCHER
{1900, 90}, // HEALER
{ 320, 20}, // TONGUE
{2700, 100 // TREASURE
} };
/* -200 common
220-400 uncommon
420-980 rare
1000+ very rare */
struct
{ SBYTE x, y, deltax, deltay, relx, rely;
BOOL alive, last, visible;
} protector[4][PROTECTORS + 1];
struct
{ SBYTE x, y, time;
BOOL alive;
} timebomb[TIMEBOMBS + 1];
struct
{ SBYTE deltax;
SBYTE deltay;
} thequeue[4][QUEUELIMIT + 1];
SBYTE eachworm[4][2][9] =
{ { { GREENHEADUP, GREENHEADUP, GREENHEADUP,
GREENHEADLEFT, ANYTHING, GREENHEADRIGHT,
GREENHEADDOWN, GREENHEADDOWN, GREENHEADDOWN
},
{ GREENMODEUP, GREENMODEUP, GREENMODEUP,
GREENMODELEFT, ANYTHING, GREENMODERIGHT,
GREENMODEDOWN, GREENMODEDOWN, GREENMODEDOWN
} },
{ { REDHEADUP, REDHEADUP, REDHEADUP,
REDHEADLEFT, ANYTHING, REDHEADRIGHT,
REDHEADDOWN, REDHEADDOWN, REDHEADDOWN
},
{ REDMODEUP, REDMODEUP, REDMODEUP,
REDMODELEFT, ANYTHING, REDMODERIGHT,
REDMODEDOWN, REDMODEDOWN, REDMODEDOWN
} },
{ { BLUEHEADUP, BLUEHEADUP, BLUEHEADUP,
BLUEHEADLEFT, ANYTHING, BLUEHEADRIGHT,
BLUEHEADDOWN, BLUEHEADDOWN, BLUEHEADDOWN
},
{ BLUEMODEUP, BLUEMODEUP, BLUEMODEUP,
BLUEMODELEFT, ANYTHING, BLUEMODERIGHT,
BLUEMODEDOWN, BLUEMODEDOWN, BLUEMODEDOWN
} },
{ { YELLOWHEADUP, YELLOWHEADUP, YELLOWHEADUP,
YELLOWHEADLEFT, ANYTHING, YELLOWHEADRIGHT,
YELLOWHEADDOWN, YELLOWHEADDOWN, YELLOWHEADDOWN
},
{ YELLOWMODEUP, YELLOWMODEUP, YELLOWMODEUP,
YELLOWMODELEFT, ANYTHING, YELLOWMODERIGHT,
YELLOWMODEDOWN, YELLOWMODEDOWN, YELLOWMODEDOWN
} } };
// MODULE VARIABLES (used only within engine.c(pp)) -----------------------
MODULE ABOOL ignorenextjoy,
letters[4][LETTERS + 1], trainer;
MODULE SBYTE fragspeed, ice, killerspeed, noletter,
orbspeed, outoftime, treasurer;
MODULE SWORD killerfreq, orbfreq, slimefreq,
slimegrowfreq;
// GLOBAL VARIABLES (owned by engine.c(pp), imported by system.c(pp)) -----
AGLOBAL ABOOL clearthem = FALSE,
modified = FALSE;
AGLOBAL SBYTE a = GAMEOVER,
board[MAXLEVELS + 1][FIELDX + 1][FIELDY + 1],
brush = STONE,
field[FIELDX + 1][FIELDY + 1], hi = 3,
level = 1, levels, lo = 0, reallevel,
startx[MAXLEVELS + 1],
starty[MAXLEVELS + 1];
AGLOBAL SWORD secondsleft, secondsperlevel;
AGLOBAL STRPTR pathname = (STRPTR) DEFAULTSET;
AGLOBAL ULONG delay, r;
AGLOBAL struct HiScoreStruct hiscore[HISCORES + 1];
AGLOBAL struct OrbStruct orb[ORBS + 1];
AGLOBAL struct TeleportStruct teleport[MAXLEVELS + 1][4];
AGLOBAL struct WormStruct worm[4];
/* FUNCTIONS --------------------------------------------------------------
NAME align -- right-justify a string within another string
SYNOPSIS align(SBYTE, SBYTE, TEXT);
FUNCTION Moves all text in a string to the right, padding with
spaces. Does not itself add a null terminator.
INPUTS string - pointer to the string of text
size - size in characters of the containing string
filler - what to pad the left of the string with
NOTE Null terminators are written over by thissy function, but that
does not matter, because calling functions use Text() with an
explicit length. This function only works with monospaced
fonts.
MODULE engine.c */
void align(STRPTR string, SBYTE size, TEXT filler)
{ SBYTE i, shift, length;
length = strlen((const char*) string);
shift = size - length;
for (i = 1; i <= length; i++)
*(string + size - i) = *(string + size - i - shift);
for (i = 0; i <= shift - 1; i++)
*(string + i) = filler;
}
ABOOL blocked(SBYTE which, SBYTE deltax, SBYTE deltay)
{ if (field[xwrap(teleport[level][partner(which)].x + deltax)][ywrap(teleport[level][partner(which)].y + deltay)] < STONE
|| field[xwrap(teleport[level][partner(which)].x + deltax)][ywrap(teleport[level][partner(which)].y + deltay)] > KILLER)
return(FALSE);
else return(TRUE);
}
void bombblast(SBYTE triggerer, SBYTE player, SBYTE centrex, SBYTE centrey)
{ SBYTE counter, downy, downymax, leftx, leftxmax, rightx, rightxmax, strength, uppy, uppymax, x, y;
LONG score = 0L;
effect(FXBOMBBLAST);
strength = BOMBADD + (rand() % BOMBRAND);
leftxmax = centrex - strength;
if (leftxmax < 0)
leftxmax = 0;
rightxmax = centrex + strength;
if (rightxmax > FIELDX)
rightxmax = FIELDX;
uppymax = centrey - strength;
if (uppymax < 0)
uppymax = 0;
downymax = centrey + strength;
if (downymax > FIELDY)
downymax = FIELDY;
leftx = centrex;
rightx = centrex;
uppy = centrey;
downy = centrey;
for (counter = 1; counter <= strength; counter++)
{ if (leftx > leftxmax)
{ leftx--;
for (y = uppy; y <= downy; y++)
score += squareblast(triggerer, player, field[leftx][y], leftx, y);
}
if (rightx < rightxmax)
{ rightx++;
for (y = uppy; y <= downy; y++)
score += squareblast(triggerer, player, field[rightx][y], rightx, y);
}
if (uppy > uppymax)
{ uppy--;
for (x = leftx; x <= rightx; x++)
score += squareblast(triggerer, player, field[x][uppy], x, uppy);
}
if (downy < downymax)
{ downy++;
for (x = leftx; x <= rightx; x++)
score += squareblast(triggerer, player, field[x][downy], x, downy);
} }
if (triggerer == HEAD)
{ wormscore(player, score);
if (worm[player].bias)
stat(player, LIVESLINE);
} else
orb[player].score += score * orb[player].multi;
}
void bouncekiller(SBYTE which, SBYTE x, SBYTE y)
{ SBYTE i;
if (field[x][y] == KILLER)
for (i = 0; i <= KILLERS; i++)
if (killer[i].alive && x == killer[i].x && y == killer[i].y)
{ killer[i].alive = FALSE;
draw(x, y, BONUS);
field[x][y] = BONUS;
orb[which].score += KILLKILLER * orb[which].multi;
} }
ABOOL bounceorb(SBYTE which, SBYTE x, SBYTE y)
{ if (orb[which].mode == NONE)
{ if (field[x][y] >= FIRSTNONE && field[x][y] <= LASTNONE)
return(TRUE);
else return(FALSE);
} else if (orb[which].mode == TONGUE)
{ if (field[x][y] >= FIRSTTONGUE && field[x][y] <= LASTTONGUE)
return(TRUE);
else return(FALSE);
} else // assumes orb[which].mode == ARMOUR
{ if (field[x][y] >= FIRSTARMOUR && field[x][y] <= LASTARMOUR)
return(TRUE);
else return(FALSE);
} }
SBYTE bsign(SBYTE value)
{ if (value < 0)
return (-1);
else if (value > 0)
return (1);
else
return (0);
}
void changefield(void)
{ SBYTE x, y;
for (x = 0; x <= FIELDX; x++)
for (y = 0; y <= FIELDY; y++)
field[x][y] = board[level][x][y];
}
void clearhiscores(void)
{ SBYTE i;
clearthem = FALSE;
for (i = 0; i <= HISCORES; i++)
{ hiscore[i].player = -1;
hiscore[i].level = 0;
hiscore[i].score = 0L;
hiscore[i].name[0] = 0;
hiscore[i].fresh = FALSE;
} }
void clearletters(void)
{ SBYTE player, which;
for (player = 0; player <= 3; player++)
for (which = 0; which <= LETTERS; which++)
{ letters[player][which] = FALSE;
drawletter(player, FIRSTLETTER + which, BLACK);
} }
void copyfield(SBYTE source, SBYTE destination)
{ SBYTE which, x, y;
for (x = 0; x <= FIELDX; x++)
for (y = 0; y <= FIELDY; y++)
board[destination][x][y] = board[source][x][y];
startx[destination] = startx[source];
starty[destination] = starty[source];
for (which = 0; which <= 1; which++)
{ teleport[destination][which].alive = teleport[source][which].alive;
teleport[destination][which].x = teleport[source][which].x;
teleport[destination][which].y = teleport[source][which].y;
} }
void death(void)
{ SBYTE counter, i, pain, player, survivor, which;
ABOOL flag, slow;
for (player = lo; player <= hi; player++)
{ if (worm[player].lives)
{ if (!worm[player].alive)
{ slow = FALSE;
pain = 0;
if (worm[player].cause >= FIRSTTAIL && worm[player].cause <= LASTTAIL)
{ if (player == worm[player].cause - FIRSTTAIL)
pain = TAILPAIN;
else pain = OTHERTAILPAIN;
slow = TRUE;
} else
{ if (worm[player].multi > 1)
worm[player].multi /= 2;
if (worm[player].cause >= FIRSTFIRE && worm[player].cause <= LASTFIRE)
pain = WORMFIREPAIN;
else if (worm[player].cause >= FIRSTHEAD && worm[player].cause <= LASTHEAD)
pain = HEADPAIN;
else if (worm[player].cause >= FIRSTPROTECTOR && worm[player].cause <= LASTPROTECTOR)
pain = PROTECTORPAIN;
else if (worm[player].cause >= FIRSTMISSILE && worm[player].cause <= LASTMISSILE)
pain = MISSILEPAIN;
else switch (worm[player].cause) {
case BOMB:
pain = BOMBPAIN;
break;
case WOOD:
pain = WOODPAIN;
slow = TRUE;
break;
case FRAGMENT:
pain = FRAGMENTPAIN;
break;
case KILLER:
pain = KILLERPAIN;
slow = TRUE;
break;
case SLAYER:
pain = SLAYERPAIN;
break;
case STONE:
pain = STONEPAIN;
slow = TRUE;
break;
case TELEPORT:
pain = TELEPORTPAIN;
slow = TRUE;
break;
case SLIME:
pain = SLIMEPAIN;
slow = TRUE;
break;
default:
break;
} }
if (worm[player].victor >= 0 && worm[player].victor != player)
{ wormscore(worm[player].victor, KILLWORM);
if (worm[worm[player].victor].bias)
{ worm[worm[player].victor].lives += pain;
stat(worm[player].victor, LIVESLINE);
} }
if (slow)
{ worm[player].speed = slowdown(worm[player].speed, worm[player].nitro);
stat(player, SPEEDLINE);
}
if (pain > worm[player].lives)
worm[player].lives = 0;
else worm[player].lives -= pain;
draw(worm[player].x, worm[player].y, SKULL);
drawcause(player, NORMAL);
stat(player, LIVESLINE);
if (level)
worm[player].levelreached = level;
else worm[player].levelreached = reallevel;
if (worm[player].lives)
{ flag = FALSE;
for (i = lo; i <= hi; i++)
if (worm[i].control == HUMAN)
flag = TRUE;
if (!flag || worm[player].control == HUMAN)
effect(FXPAIN);
// Amiga-worms only make pain sounds in demo mode
worm[player].alive = TRUE;
worm[player].causewait = r + CAUSEWAIT;
} else
{ // kill worm
if (worm[player].tonguereceipt)
{ stopfx(worm[player].tonguereceipt);
worm[player].tonguereceipt = 0L;
}
effect(FXWORMDEATH);
if (ice == player)
ice = -1;
field[worm[player].x][worm[player].y] = SKULL;
for (which = 0; which <= PROTECTORS; which++)
{ if (protector[player][which].alive && protector[player][which].visible)
{ draw(protector[player][which].x, protector[player][which].y, EMPTY);
field[protector[player][which].x][protector[player][which].y] = EMPTY;
} }
counter = 0;
for (which = lo; which <= hi; which++)
if (worm[which].lives)
{ survivor = which;
counter++;
}
if (counter == 1)
wormscore(survivor, SURVIVOR);
} } } }
if (!worm[0].lives && !worm[1].lives && !worm[2].lives && !worm[3].lives)
{ // End of game
newhiscores();
effect(FXDEFEAT);
a = GAMEOVER;
if (lo == hi)
say((STRPTR) "Game over!", worm[lo].colour);
else if (worm[0].control && ((!worm[1].control) || worm[1].score < worm[0].score) && ((!worm[2].control) || worm[2].score < worm[0].score) && ((!worm[3].control) || worm[3].score < worm[0].score))
say((STRPTR) "Green wins!", GREEN);
else if (worm[1].control && ((!worm[0].control) || worm[0].score < worm[1].score) && ((!worm[2].control) || worm[2].score < worm[1].score) && ((!worm[3].control) || worm[3].score < worm[1].score))
say((STRPTR) "Red wins!", RED);
else if (worm[2].control && ((!worm[0].control) || worm[0].score < worm[2].score) && ((!worm[1].control) || worm[1].score < worm[2].score) && ((!worm[3].control) || worm[3].score < worm[2].score))
say((STRPTR) "Blue wins!", BLUE);
else if (worm[3].control && ((!worm[0].control) || worm[0].score < worm[3].score) && ((!worm[1].control) || worm[1].score < worm[3].score) && ((!worm[2].control) || worm[2].score < worm[3].score))
say((STRPTR) "Yellow wins!", YELLOW);
else say((STRPTR) "A draw!", WHITE);
waitasec();
anykey(FALSE);
} }
void drawcause(SBYTE player, SBYTE state)
{ if (state == BLACK)
blitmode(BLACK);
draw(-2 + ((FIELDX + 4) * worm[player].statx), (FIELDY / 2) - CAUSEYDISTANCE + (worm[player].staty * CAUSEYDISTANCE * 2), worm[player].cause);
if (state == BLACK)
blitmode(NORMAL);
}
void drawletter(SBYTE player, SBYTE letter, SBYTE state)
{ if (state == BLACK)
blitmode(BLACK);
if (!worm[player].statx)
if (!worm[player].staty)
draw(-7 + ((letter - FIRSTLETTER) % 4),
(FIELDY / 2) - 2 + ((letter - FIRSTLETTER) / 4),
letter);
else
draw(-7 + ((letter - FIRSTLETTER) % 4),
(FIELDY / 2) + 1 + ((letter - FIRSTLETTER) / 4),
letter);
else
if (!worm[player].staty)
draw(FIELDX + 4 + ((letter - FIRSTLETTER) % 4),
(FIELDY / 2) - 2 + ((letter - FIRSTLETTER) / 4),
letter);
else
draw(FIELDX + 4 + ((letter - FIRSTLETTER) % 4),
(FIELDY / 2) + 1 + ((letter - FIRSTLETTER) / 4),
letter);
if (state == BLACK)
blitmode(NORMAL);
}
/* NAME enginesetup -- once-only initialization of engine variables
SYNOPSIS enginesetup(void);
FUNCTION Sets up the unchanging worm variables.
MODULE engine.c */
void enginesetup(void)
{ worm[0].statx = worm[0].staty = worm[1].staty = worm[2].statx = 0;
worm[1].statx = worm[2].staty = worm[3].statx = worm[3].staty = 1;
worm[0].colour = GREEN;
worm[1].colour = RED;
worm[2].colour = BLUE;
worm[3].colour = YELLOW;
worm[0].name[0] = worm[1].name[0] = worm[2].name[0] = worm[3].name[0] = 0;
systemsetup();
}
/* NAME fastloop -- things done often
SYNOPSIS fastloop(void);
FUNCTION Checks for and handles level completion.
MODULE engine.c */
void fastloop(void)
{ ABOOL complete;
SBYTE advancer = -1, player, which;
// handle level completion
for (player = lo; player <= hi; player++)
{ complete = TRUE;
for (which = 0; which <= LETTERS; which++)
if (!letters[player][which])
complete = FALSE;
if (complete)
advancer = player;
}
if (advancer != -1)
{ if (level++ == 0)
{ level = reallevel + 1;
reallevel = 0;
}
stopfx(0L);
if (level > levels)
effect(FXVICTORY);
else if (level >= 2)
effect(FXENDOFLEVEL);
newlevel(advancer);
} }
void fillfield(SBYTE which)
{ SBYTE x, y;
for (x = 0; x <= FIELDX; x++)
for (y = 0; y <= FIELDY; y++)
{ board[level][x][y] = which;
draw(x, y, which);
}
board[level][startx[level]][starty[level]] = EMPTY;
draw(startx[level], starty[level], START);
if (teleport[level][0].alive)
{ board[level][teleport[level][0].x][teleport[level][0].y] = TELEPORT;
draw(teleport[level][0].x, teleport[level][0].y, ONE);
}
if (teleport[level][1].alive)
{ board[level][teleport[level][1].x][teleport[level][1].y] = TELEPORT;
draw(teleport[level][1].x, teleport[level][1].y, TWO);
} }
ABOOL findempty(SBYTE* x, SBYTE* y, SBYTE first, SBYTE last)
{ SBYTE count = 0, xx, yy;
do
{ xx = rand() % (FIELDX + 1);
yy = rand() % (FIELDY + 1);
} while ((field[xx][yy] < first || field[xx][yy] > last) && ++count < PATIENCE);
if (count < PATIENCE)
{ *x = xx;
*y = yy;
return(TRUE);
} else return(FALSE);
}
/* NAME fragloop -- controls fragments
SYNOPSIS fragloop(void);
FUNCTION Controls all fragments.
MODULE engine.c */
void fragloop(void)
{ SBYTE i, j, k, thissy, which;
for (which = 0; which <= ORBS + 1; which++)
for (i = 0; i <= 7; i++)
if (frag[which][i].alive)
{ if (frag[which][i].moved)
{ draw(frag[which][i].x, frag[which][i].y, frag[which][i].last);
field[frag[which][i].x][frag[which][i].y] = frag[which][i].last;
} else frag[which][i].moved = TRUE;
frag[which][i].x += frag[which][i].deltax;
frag[which][i].y += frag[which][i].deltay;
if (!(valid(frag[which][i].x, frag[which][i].y)))
frag[which][i].alive = FALSE;
else
{ thissy = field[frag[which][i].x][frag[which][i].y];
if (thissy >= FIRSTFRAGDEATH && thissy <= LASTFRAGDEATH)
{ effect(FXTHUD);
frag[which][i].alive = FALSE;
if (thissy == KILLER)
{ effect(FXKILLERDEATH);
j = whichkiller(frag[which][i].x, frag[which][i].y);
killer[j].alive = FALSE;
field[killer[j].x][killer[j].y] = BONUS;
draw(killer[j].x, killer[j].y, BONUS);
} }
else if (thissy >= FIRSTPROTECTOR && thissy <= LASTPROTECTOR)
{ effect(FXUSEPROTECTOR);
reflect(which, i);
frag[which][i].x += frag[which][i].deltax * 2;
frag[which][i].y += frag[which][i].deltay * 2;
} else if (thissy >= FIRSTMISSILE && thissy <= LASTMISSILE)
{ frag[which][i].alive = FALSE;
stopfx(missile[thissy - FIRSTMISSILE].receipt);
missile[thissy - FIRSTMISSILE].alive = FALSE;
field[missile[thissy - FIRSTMISSILE].x][missile[thissy - FIRSTMISSILE].y] = EMPTY;
draw(missile[thissy - FIRSTMISSILE].x, missile[thissy - FIRSTMISSILE].y, EMPTY);
} else if (thissy >= FIRSTHEAD && thissy <= LASTHEAD)
{ if (worm[thissy - FIRSTHEAD].mode != ARMOUR)
{ worm[thissy - FIRSTHEAD].cause = FRAGMENT;
worm[thissy - FIRSTHEAD].victor = -1;
worm[thissy - FIRSTHEAD].alive = FALSE;
} else
{ effect(FXUSEARMOUR);
reflect(which, i);
} }
else switch(thissy) {
case ORB:
j = whichorb(frag[which][i].x, frag[which][i].y);
if (orb[j].mode != ARMOUR)
orb[j].explode = TRUE;
else effect(FXUSEARMOUR);
frag[which][i].alive = FALSE;
break;
case TELEPORT:
j = whichteleport(frag[which][i].x, frag[which][i].y);
if (blocked(j, frag[which][i].deltax, frag[which][i].deltay))
frag[which][i].alive = FALSE;
else
{ effect(FXUSETELEPORT);
frag[which][i].x = xwrap(teleport[level][partner(j)].x + frag[which][i].deltax);
frag[which][i].y = ywrap(teleport[level][partner(j)].y + frag[which][i].deltay);
frag[which][i].last = SILVER;
}
break;
case BOMB:
j = whichtimebomb(frag[which][i].x, frag[which][i].y);
if (j != -1)
{ frag[which][i].alive = FALSE;
timebomb[j].alive = FALSE;
field[timebomb[j].x][timebomb[j].y] = EMPTY;
draw(timebomb[j].x, timebomb[j].y, EMPTY);
bombblast(BOMB, 0, timebomb[j].x, timebomb[j].y);
}
break;
case FRAGMENT:
effect(FXTHUD);
draw(frag[which][i].x, frag[which][i].y, EMPTY);
frag[which][i].alive = FALSE;
whichfrag(frag[which][i].x, frag[which][i].y, &j, &k);
frag[j][k].alive = FALSE;
break;
default:
break;
}
if (frag[which][i].alive)
{ field[frag[which][i].x][frag[which][i].y] = FRAGMENT;
draw(frag[which][i].x, frag[which][i].y, FRAGMENT);
} } }
}
void gameloop(void)
{ SBYTE player, which;
fastloop();
gameinput();
for (player = lo; player <= hi; player++)
if (worm[player].lives && !(r % worm[player].speed) && (ice == -1 || ice == player))
wormloop(player);
if (ice == -1)
{ for (which = 0; which <= ORBS; which++)
if (orb[which].alive && !(r % orb[which].speed))
if (orb[which].explode)
orbexplosion(which);
else orbloop(which);
if (!(r % killerspeed))
killerloop();
if (!(r % fragspeed))
fragloop();
if (!(r % MISSILESPEED))
missileloop();
}
if (a == PLAYGAME)
{ death();
if (!(r % VERYSLOW))
slowloop();
} }
void killall(void)
{ SBYTE iwhich, which;
for (which = 0; which <= ORBS; which++)
orb[which].alive = FALSE;
for (which = 0; which <= KILLERS; which++)
killer[which].alive = FALSE;
for (which = 0; which <= ORBS + 1; which++)
for (iwhich = 0; iwhich <= 7; iwhich++)
frag[which][iwhich].alive = FALSE;
for (which = 0; which <= 3; which++)
if (missile[which].alive)
{ stopfx(missile[which].receipt);
missile[which].alive = FALSE;
}
for (which = 0; which <= TIMEBOMBS; which++)
timebomb[which].alive = FALSE;
teleport[level][2].alive = FALSE;
teleport[level][3].alive = FALSE;
}
void killerloop(void)
{ ABOOL happy;
UBYTE count;
SBYTE which, x, xx, y, yy;
for (which = 0; which <= KILLERS; which++)
if (killer[which].alive)
{ happy = FALSE;
for (x = killer[which].x - 1; x <= killer[which].x + 1; x++)
for (y = killer[which].y - 1; y <= killer[which].y + 1; y++)
if (field[x][y] >= FIRSTEMPTY && field[x][y] <= LASTEMPTY)
happy = TRUE;
if ((!happy) || (!(rand() % RESTFULNESS)))
{ x = (rand() % 3) - 1;
y = (rand() % 3) - 1;
if (valid(killer[which].x + x, killer[which].y + y) && (x || y)
&& field[killer[which].x + x][killer[which].y + y] >= FIRSTKILLER && field[killer[which].x + x][killer[which].y + y] <= LASTKILLER && field[killer[which].x + x][killer[which].y + y] != KILLER)
{ field[killer[which].x][killer[which].y] = killer[which].last;
draw(killer[which].x, killer[which].y, killer[which].last);
killer[which].x += x;
killer[which].y += y;
killer[which].last = field[killer[which].x][killer[which].y];
field[killer[which].x][killer[which].y] = KILLER;
draw(killer[which].x, killer[which].y, KILLER);
} }
if (!frag[ORBS + 1][which].alive && !(rand() % KILLERFIREFREQ))
{ count = 0;
do
{ x = (rand() % 3) - 1;
y = (rand() % 3) - 1;
xx = killer[which].x + x;
yy = killer[which].y + y;
} while (((!x && !y) || xx < 0 || xx > FIELDX || yy < 0 || yy > FIELDY || field[xx][yy] > LASTKILLERFIRE) && ++count < PATIENCE);
if (count < PATIENCE)
{ effect(FXKILLERFIRE);
frag[ORBS + 1][which].alive = TRUE;
frag[ORBS + 1][which].x = killer[which].x;
frag[ORBS + 1][which].y = killer[which].y;
frag[ORBS + 1][which].deltax = x;
frag[ORBS + 1][which].deltay = y;
frag[ORBS + 1][which].moved = FALSE;
frag[ORBS + 1][which].last = EMPTY;
} } } }
void levelappend(void)
{ UBYTE oldlevel;
if (levels < MAXLEVELS)
{ oldlevel = level;
level = ++levels;
newfield();
level = oldlevel;
saylevel(WHITE);
} }
void leveldelete(void)
{ SBYTE i;
// pull boards
if (levels > 1)
{ if (level < levels)
for (i = level; i < levels; i++)
copyfield(i + 1, i);
else
level--;
levels--;
saylevel(WHITE);
renderboard();
} }
void levelerase(void)
{ newfield();
renderboard();
}
void levelinsert(void)
{ SBYTE i;
// push boards
if (levels < MAXLEVELS)
{ for (i = levels; i >= level; i--)
copyfield(i, i + 1);
levels++;
saylevel(WHITE);
newfield();
renderboard();
} }
ABOOL loadfields(STRPTR fieldname)
{ SBYTE i, j;
TEXT IOBuffer[NAMELENGTH + 1];
// open file
if (!ZOpen(fieldname, FALSE))
return FALSE;
// read header
if (!ZRead(IOBuffer, 10))
{ ZClose();
return FALSE;
}
if (strcmp(IOBuffer, "FSET 4.1"))
{ ZClose();
return FALSE;
}
levels = IOBuffer[9];
// read high score table
for (i = 0; i <= HISCORES; i++)
{ if (!ZRead(IOBuffer, 6))
{ ZClose();
return FALSE;
}
hiscore[i].fresh = FALSE;
hiscore[i].player = IOBuffer[0];
hiscore[i].level = IOBuffer[1];
hiscore[i].score = (IOBuffer[3] * 65536)
+ (IOBuffer[4] * 256)
+ IOBuffer[5];
if (!ZRead(IOBuffer, NAMELENGTH + 1))
{ ZClose();
return FALSE;
}
for (j = 0; j <= NAMELENGTH; j++)
hiscore[i].name[j] = IOBuffer[j];
}
// read level data
for (i = 0; i <= levels; i++)
{ if (!ZRead(IOBuffer, 8))
{ ZClose();
return FALSE;
}
startx[i] = IOBuffer[0];
starty[i] = IOBuffer[1];
teleport[i][0].alive = IOBuffer[2];
teleport[i][0].x = IOBuffer[3];
teleport[i][0].y = IOBuffer[4];
teleport[i][1].alive = IOBuffer[5];
teleport[i][1].x = IOBuffer[6];
teleport[i][1].y = IOBuffer[7];
if (!ZRead((char *) &board[i][0][0], (FIELDX + 1) * (FIELDY + 1)))
{ ZClose();
return FALSE;
} }
// no need to read version string
ZClose();
modified = FALSE;
return TRUE;
}
void matchteleports(void)
{ SBYTE which;
for (which = 0; which <= levels; which++)
if (teleport[which][0].alive == TRUE && teleport[which][1].alive == FALSE)
{ board[which][teleport[which][0].x][teleport[which][0].y] = EMPTY;
teleport[which][0].alive = FALSE;
if (level == which && a == FIELDEDIT)
draw(teleport[which][0].x, teleport[which][0].y, EMPTY);
} else if (teleport[which][0].alive == FALSE && teleport[which][1].alive == TRUE)
{ board[which][teleport[which][1].x][teleport[which][1].y] = EMPTY;
teleport[which][1].alive = FALSE;
if (level == which && a == FIELDEDIT)
draw(teleport[which][1].x, teleport[which][1].y, EMPTY);
} }
/* NAME missileloop -- controls missiles
SYNOPSIS missileloop(void);
FUNCTION Controls all killers.
MODULE engine.c */
void missileloop(void)
{ SBYTE distx, disty, distance, iwhich, player, thissy, which;
UBYTE bestdistance;
for (player = lo; player <= hi; player++)
{ if (missile[player].alive && (ice == -1 || ice == player))
{ bestdistance = (UBYTE) -1;
for (which = lo; which <= hi; which++)
if (which != player && worm[which].lives > 0 && !worm[which].bias)
{ distx = abs(worm[which].x - missile[player].x);
disty = abs(worm[which].y - missile[player].y);
if (distx < disty)
distance = distx;
else distance = disty;
if (distance <= bestdistance)
{ bestdistance = distance;
missile[player].deltax = bsign(worm[which].x - missile[player].x);
missile[player].deltay = bsign(worm[which].y - missile[player].y);
} }
for (which = lo; which <= hi; which++)
if (which != player && missile[which].alive)
{ distx = abs(missile[which].x - missile[player].x);
disty = abs(missile[which].y - missile[player].y);
if (distx < disty)
distance = distx;
else distance = disty;
if (distance <= bestdistance)
{ bestdistance = distance;
missile[player].deltax = bsign(missile[which].x - missile[player].x);
missile[player].deltay = bsign(missile[which].y - missile[player].y);
} }
for (which = 0; which <= ORBS; which++)
if (orb[which].alive)
{ distx = abs(orb[which].x - missile[player].x) * 2;
disty = abs(orb[which].y - missile[player].y) * 2;
if (distx < disty)
distance = distx;
else distance = disty;
if (distance <= bestdistance)
{ bestdistance = distance;
missile[player].deltax = bsign(orb[which].x - missile[player].x);
missile[player].deltay = bsign(orb[which].y - missile[player].y);
} }
for (which = 0; which <= KILLERS; which++)
if (killer[which].alive)
{ distx = abs(killer[which].x - missile[player].x) * 2;
disty = abs(killer[which].y - missile[player].y) * 2;
if (distx < disty)
distance = distx;
else distance = disty;
if (distance <= bestdistance)
{ bestdistance = distance;
missile[player].deltax = bsign(killer[which].x - missile[player].x);
missile[player].deltay = bsign(killer[which].y - missile[player].y);
} }
if (missile[player].moved)
{ draw(missile[player].x, missile[player].y, EMPTY);
field[missile[player].x][missile[player].y] = EMPTY;
} else missile[player].moved = TRUE;
if (bestdistance == (UBYTE) -1)
{ stopfx(missile[player].receipt);
missile[player].alive = FALSE;
} else
{ missile[player].x += missile[player].deltax;
missile[player].y += missile[player].deltay;
thissy = field[missile[player].x][missile[player].y];
if (thissy >= FIRSTLETTER && thissy <= WOOD)
{ stopfx(missile[player].receipt);
missile[player].alive = FALSE;
if (thissy == KILLER)
{ effect(FXKILLERDEATH);
which = whichkiller(missile[player].x, missile[player].y);
killer[which].alive = FALSE;
field[killer[which].x][killer[which].y] = BONUS;
draw(killer[which].x, killer[which].y, BONUS);
wormscore(player, KILLKILLER);
if (worm[player].bias)
{ worm[player].lives += KILLERBLOOD;
stat(player, LIVESLINE);
} } }
else if (thissy == FRAGMENT)
{ stopfx(missile[player].receipt);
missile[player].alive = FALSE;
whichfrag(missile[player].x, missile[player].y, &which, &iwhich);
frag[which][iwhich].alive = FALSE;
} else if (thissy == ORB)
{ stopfx(missile[player].receipt);
missile[player].alive = FALSE;
which = whichorb(missile[player].x, missile[player].y);
if (orb[which].mode != ARMOUR)
{ effect(FXORBDEATH);
orb[which].alive = FALSE;
draw(orb[which].x, orb[which].y, BONUS);
field[orb[which].x][orb[which].y] = BONUS;
wormscore(player, orb[which].score);
if (worm[player].bias)
{ worm[player].lives += ORBBLOOD;
stat(player, LIVESLINE);
}
} else effect(FXUSEARMOUR);
} else if (thissy >= FIRSTPROTECTOR && thissy <= LASTPROTECTOR)
{ if (player != thissy - FIRSTPROTECTOR)
{ stopfx(missile[player].receipt);
effect(FXUSEPROTECTOR);
missile[player].alive = FALSE;
} }
else if (thissy >= FIRSTHEAD && thissy <= LASTHEAD)
{ stopfx(missile[player].receipt);
missile[player].alive = FALSE;
if (worm[thissy - FIRSTHEAD].mode != ARMOUR)
{ worm[thissy - FIRSTHEAD].cause = FIRSTMISSILE + player;
worm[thissy - FIRSTHEAD].victor = player;
worm[thissy - FIRSTHEAD].alive = FALSE;
} else effect(FXUSEARMOUR);
} else if (thissy >= FIRSTMISSILE && thissy <= LASTMISSILE)
{ stopfx(missile[player].receipt);
stopfx(missile[thissy - FIRSTMISSILE].receipt);
missile[player].alive = FALSE;
missile[thissy - FIRSTMISSILE].alive = FALSE;
field[missile[which].x][missile[which].y] = BONUS;
draw(missile[which].x, missile[which].y, BONUS);
} else if (thissy == TELEPORT)
{ which = whichteleport(missile[player].x, missile[player].y);
if (blocked(which, missile[player].deltax, missile[player].deltay))
{ stopfx(missile[player].receipt);
missile[player].alive = FALSE;
} else
{ effect(FXUSETELEPORT);
missile[player].x = xwrap(teleport[level][partner(which)].x + missile[player].deltax);
missile[player].y = ywrap(teleport[level][partner(which)].y + missile[player].deltay);
} }
else if (thissy == BOMB)
{ which = whichtimebomb(missile[player].x, missile[player].y);
if (which != -1)
{ stopfx(missile[player].receipt);
missile[player].alive = FALSE;
timebomb[which].alive = FALSE;
field[timebomb[which].x][timebomb[which].y] = EMPTY;
draw(timebomb[which].x, timebomb[which].y, EMPTY);
bombblast(BOMB, 0, timebomb[which].x, timebomb[which].y);
} }
else if (thissy == SKULL)
{ stopfx(missile[player].receipt);
missile[player].alive = FALSE;
}
if (missile[player].alive)
{ draw(missile[player].x, missile[player].y, FIRSTMISSILE + player);
field[missile[player].x][missile[player].y] = FIRSTMISSILE + player;
} } } } }
void newfield(void)
{ int x, y;
teleport[level][0].alive = FALSE;
teleport[level][1].alive = FALSE;
startx[level] = FIELDX / 2;
starty[level] = FIELDY / 2;
if (level)
for (x = 0; x <= FIELDX; x++)
for (y = 0; y <= FIELDY; y++)
board[level][x][y] = EMPTY;
else for (x = 0; x <= FIELDX; x++)
for (y = 0; y <= FIELDY; y++)
board[0][x][y] = SILVER;
}
void newfields(void)
{ if (verify())
{ pathname = (STRPTR) DEFAULTSET;
levels = DEFAULTLEVELS;
modified = FALSE;
for (level = 0; level <= levels; level++)
newfield();
clearhiscores();
level = 1;
if (a == FIELDEDIT)
{ renderboard();
saylevel(WHITE);
} else
hiscores();
} }
void newgame(void)
{ SBYTE player;
if (worm[0].control != NONE)
lo = 0;
else if (worm[1].control != NONE)
lo = 1;
else if (worm[2].control != NONE)
lo = 2;
else lo = 3;
if (worm[3].control != NONE)
hi = 3;
else if (worm[2].control != NONE)
hi = 2;
else if (worm[1].control != NONE)
hi = 1;
else hi = 0;
for (player = 0; player <= 3; player++)
worm[player].lives = 0;
r = -1;
trainer = FALSE;
ice = -1;
reallevel = 0;
level = 1;
a = PLAYGAME;
clearstats();
newlevel(rand() % 4);
}
void newhiscores(void)
{
PERSIST TEXT amiganame[4][NAMELENGTH + 1] = {"Jay Miner", "Carl Sassenrath", "R.J. Mical", "Dave Morse"};
SBYTE iwhich, player, which;
for (player = lo; player <= hi; player++)
for (which = 0; which <= HISCORES; which++)
if (worm[player].control != NONE && worm[player].score >= hiscore[which].score)
{ // push all worse hiscores down
if (which < HISCORES)
for (iwhich = HISCORES; iwhich >= which + 1; iwhich--)
{ hiscore[iwhich].player = hiscore[iwhich - 1].player;
hiscore[iwhich].score = hiscore[iwhich - 1].score;
hiscore[iwhich].level = hiscore[iwhich - 1].level;
hiscore[iwhich].fresh = hiscore[iwhich - 1].fresh;
strcpy(hiscore[iwhich].name, hiscore[iwhich - 1].name);
}
modified = TRUE;
hiscore[which].player = player;
hiscore[which].score = worm[player].score;
hiscore[which].level = worm[player].levelreached;
if (worm[player].control == AMIGA)
{ strcpy(hiscore[which].name, amiganame[player]);
hiscore[which].fresh = FALSE;
} else
{ strcpy(hiscore[which].name, "(New)");
hiscore[which].fresh = TRUE;
}
break;
} }
void newlevel(SBYTE player)
{ SBYTE iwhich, which;
if (!level)
{ delay = GAMEDELAY;
fragspeed = FRAGSPEED;
killerspeed = KILLERSPEED;
orbspeed = ORBSPEED;
killerfreq = KILLERFREQSTART;
orbfreq = ORBFREQSTART;
slimefreq = SLIMEFREQSTART;
slimegrowfreq = SLIMEGROWFREQSTART;
} else if (level >= 2 && worm[player].lives)
rundown(player);
if (a == PLAYGAME)
{ if (level > levels)
{ for (which = lo; which <= hi; which++)
if (worm[which].lives)
{ wormscore(which, CHAMPION);
worm[which].levelreached = -1;
}
celebrate();
newhiscores();
titlescreen();
} else
{ killall();
clearletters();
if (worm[player].lives)
saylevel(worm[player].colour);
else saylevel(WHITE);
orientworms();
changefield();
renderboard();
if (level)
{ if (level <= FASTESTLEVEL)
delay = GAMEDELAY - (level * GAMEDELAYPERLEVEL);
putletter(-1);
fragspeed = FRAGSPEED - (level / FRAGINCLEVELS);
if (fragspeed < VERYFAST)
fragspeed = VERYFAST;
orbspeed = ORBSPEED - (level / ORBINCLEVELS);
if (orbspeed < VERYFAST)
orbspeed = VERYFAST;
killerspeed = KILLERSPEED - (level / KILLERINCLEVELS);
if (killerspeed < VERYFAST)
killerspeed = VERYFAST;
killerfreq = KILLERFREQSTART - (KILLERFREQMOD * level);
if (killerfreq < KILLERFREQEND)
killerfreq = KILLERFREQEND;
orbfreq = ORBFREQSTART - (ORBFREQMOD * level);
if (orbfreq < ORBFREQEND)
orbfreq = ORBFREQEND;
slimefreq = SLIMEFREQSTART - (SLIMEFREQMOD * level);
if (slimefreq < SLIMEFREQEND)
slimefreq = SLIMEFREQEND;
slimegrowfreq = SLIMEGROWFREQSTART - (SLIMEGROWFREQMOD * level);
if (slimegrowfreq < SLIMEGROWFREQEND)
slimegrowfreq = SLIMEGROWFREQEND;
secondsperlevel = SECONDSPERLEVEL;
for (which = 0; which <= 3; which++)
{ if (!worm[which].lives && worm[which].control != NONE)
{ // create (or resurrect) a worm
worm[which].lives = STARTLIVES;
worm[which].score = 0;
worm[which].alive = TRUE;
worm[which].oldscore = 0;
worm[which].armour = 0;
worm[which].tongue = 0;
worm[which].nitro = FALSE;
worm[which].mode = NULL;
worm[which].power = 0;
worm[which].bias = 0;
worm[which].multi = 1;
worm[which].ice = 0;
worm[which].victor = -1;
worm[which].ammo = 0;
worm[which].affixer = FALSE;
worm[which].causewait = (ULONG) -1;
worm[which].last = FIRSTTAIL + which;
worm[which].pos = -1;
for (iwhich = 0; iwhich <= PROTECTORS; iwhich++) protector[which][iwhich].alive = FALSE;
for (iwhich = 0; iwhich <= LINES; iwhich++)
stat(which, iwhich);
} } } } }
clearjoystick();
clearkybd();
ignorenextjoy = FALSE;
outoftime = 0;
resettime();
}
void orbexplosion(SBYTE which)
{ SBYTE iwhich;
effect(FXEXPLODE);
orb[which].alive = FALSE;
for (iwhich = 0; iwhich <= 7; iwhich++)
{ frag[which][iwhich].alive = TRUE;
frag[which][iwhich].x = orb[which].x;
frag[which][iwhich].y = orb[which].y;
frag[which][iwhich].moved = TRUE;
frag[which][iwhich].last = EMPTY;
switch (iwhich)
{
case 0:
frag[which][iwhich].deltax = 0;
frag[which][iwhich].deltay = -1;
break;
case 1:
frag[which][iwhich].deltax = 1;
frag[which][iwhich].deltay = -1;
break;
case 2:
frag[which][iwhich].deltax = 1;
frag[which][iwhich].deltay = 0;
break;
case 3:
frag[which][iwhich].deltax = 1;
frag[which][iwhich].deltay = 1;
break;
case 4:
frag[which][iwhich].deltax = 0;
frag[which][iwhich].deltay = 1;
break;
case 5:
frag[which][iwhich].deltax = -1;
frag[which][iwhich].deltay = 1;
break;
case 6:
frag[which][iwhich].deltax = -1;
frag[which][iwhich].deltay = 0;
break;
case 7:
frag[which][iwhich].deltax = -1;
frag[which][iwhich].deltay = -1;
break;
default:
break;
} } }
/* NAME orbloop -- controls orbs
SYNOPSIS orbloop(SBYTE);
FUNCTION Controls an orb.
MODULE engine.c */
void orbloop(SBYTE which) {
SBYTE frontx, fronty, iiwhich, iwhich, player, newdeltax, newdeltay,
rearx, reary, thissy, x, xx, y, yy;
ULONG score = 0L;
// erase previous image
if (orb[which].moved)
if (field[orb[which].x][orb[which].y] != ORB)
orb[which].moved = FALSE;
else
{ draw(orb[which].x, orb[which].y, orb[which].last);
field[orb[which].x][orb[which].y] = orb[which].last;
}
else orb[which].moved = TRUE;
// bounce/move
frontx = xwrap(orb[which].x + orb[which].deltax); // look in front
fronty = ywrap(orb[which].y + orb[which].deltay);
rearx = xwrap(orb[which].x - orb[which].deltax); // look behind
reary = ywrap(orb[which].y - orb[which].deltay);
if (bounceorb(which, frontx, fronty))
{ bouncekiller(which, frontx, fronty);
newdeltax = -orb[which].deltax; // default bounce angle is 180°
newdeltay = -orb[which].deltay;
if (!bounceorb(which, frontx, reary))
{ if (bounceorb(which, rearx, fronty))
{ bouncekiller(which, rearx, fronty);
newdeltax = orb[which].deltax;
} }
else if (!bounceorb(which, rearx, fronty))
{ bouncekiller(which, rearx, fronty);
newdeltay = orb[which].deltay;
}
orb[which].deltax = newdeltax;
orb[which].deltay = newdeltay;
}
orb[which].x = xwrap(orb[which].x + orb[which].deltax);
orb[which].y = ywrap(orb[which].y + orb[which].deltay);
orb[which].last = EMPTY;
// collision detection
if (orb[which].alive)
{ x = orb[which].x;
y = orb[which].y;
thissy = field[x][y];
if (thissy <= LASTOBJECT)
{ score = object[thissy].score;
if (thissy != SLAYER && thissy != BOMB)
effect(FXGETOBJECT);
if (thissy == AMMO || thissy == SLAYER)
{ if (orb[which].mode != ARMOUR)
orb[which].explode = TRUE;
else effect(FXUSEARMOUR);
} else if (thissy == NITRO || thissy == POWER)
{ effect(FXGETNITRO);
orb[which].speed = speedup(orb[which].speed, TRUE);
} else if (thissy == LIFE || thissy == ICE || thissy == TREASURE)
orbsplit(which);
else switch(thissy) {
case ARMOUR:
orb[which].armour += MODEADD + (rand() % MODERAND);
orb[which].mode = ARMOUR;
break;
case TONGUE:
orb[which].tongue += MODEADD + (rand() % MODERAND);
orb[which].mode = TONGUE;
break;
case BOMB:
iwhich = whichtimebomb(x, y);
timebomb[iwhich].alive = FALSE;
draw(x, y, ORB); /* so the user understands what is happening
*before* the s-l-o-w bombblast() occurs */
bombblast(ORB, which, x, y);
break;
case PROTECTOR:
for (player = lo; player <= hi; player++)
if (worm[player].lives)
for (iwhich = 0; iwhich <= PROTECTORS; iwhich++)
if (protector[player][iwhich].alive)
{ protector[player][iwhich].alive = FALSE;
if (protector[player][iwhich].visible)
{ draw(protector[player][iwhich].x, protector[player][iwhich].y, EMPTY);
field[protector[player][iwhich].x][protector[player][iwhich].y] = EMPTY;
} }
break;
case MISSILE:
for (player = lo; player <= hi; player++)
if (missile[player].alive)
{ missile[player].alive = FALSE;
draw(missile[player].x, missile[player].y, EMPTY);
field[missile[player].x][missile[player].y] = EMPTY;
}
break;
case MULTIPLIER:
orb[which].multi *= 2;
if (orb[which].multi > MULTILIMIT)
orb[which].multi = MULTILIMIT;
break;
case BIAS:
for (player = lo; player <= hi; player++)
if (worm[player].lives && worm[player].bias)
{ worm[player].bias = 0;
stat(player, BIASLINE);
}
break;
case AFFIXER:
for (player = lo; player <= hi; player++)
if (worm[player].lives)
worm[player].affixer = FALSE;
break;
case SWITCHER:
for (x = 0; x <= FIELDX; x++)
for (y = 0; y <= FIELDY; y++)
if (field[x][y] >= FIRSTTAIL && field[x][y] <= LASTTAIL)
{ field[x][y] = WOOD;
draw(x, y, WOOD);
}
break;
case GROWER:
effect(FXGETGROWER);
for (x = 0; x <= FIELDX; x++)
for (y = 0; y <= FIELDY; y++)
if (field[x][y] == WOOD)
for (xx = x - 1; xx <= x + 1; xx++)
for (yy = y - 1; yy <= y + 1; yy++)
if (valid(xx, yy) && field[xx][yy] == EMPTY)
field[xx][yy] = TEMPWOOD;
for (x = 0; x <= FIELDX; x++)
for (y = 0; y <= FIELDY; y++)
if (field[x][y] == TEMPWOOD)
{ field[x][y] = WOOD;
draw(x, y, WOOD);
}
break;
default:
break;
} }
else if (thissy >= FIRSTPROTECTOR && thissy <= LASTPROTECTOR)
{ effect(FXUSEPROTECTOR);
effect(FXORBDEATH);
orb[which].alive = FALSE;
wormscore(thissy - FIRSTPROTECTOR, orb[which].score);
if (worm[thissy - FIRSTPROTECTOR].bias)
{ worm[thissy - FIRSTPROTECTOR].lives += ORBBLOOD;
stat(thissy - FIRSTPROTECTOR, LIVESLINE);
} }
else if (thissy >= FIRSTTAIL && thissy <= LASTTAIL)
{ if (orb[which].mode == TONGUE)
{ effect(FXUSETONGUE);
if (worm[thissy - FIRSTTAIL].lives && worm[thissy - FIRSTTAIL].bias)
{ orb[which].last = GOLD;
score = TURNTOGOLD;
} else
{ orb[which].last = SILVER;
score = TURNTOSILVER;
} }
else
{ if (worm[thissy - FIRSTTAIL].alive)
{ effect(FXORBDEATH);
wormscore(thissy - FIRSTTAIL, orb[which].score);
if (worm[thissy - FIRSTTAIL].bias)
{ worm[thissy - FIRSTTAIL].lives += ORBBLOOD;
stat(thissy - FIRSTTAIL, LIVESLINE);
} }
orb[which].alive = FALSE;
field[x][y] = BONUS;
draw(x, y, BONUS);
} }
else if (thissy >= FIRSTHEAD && thissy <= LASTHEAD)
{ if (worm[thissy - FIRSTHEAD].mode != ARMOUR)
{ worm[thissy - FIRSTHEAD].cause = ORB;
worm[thissy - FIRSTHEAD].victor = -1;
worm[thissy - FIRSTHEAD].alive = FALSE;
if (orb[which].mode == ARMOUR)
{ effect(FXUSEARMOUR);
score = KILLWORM;
} else
{ effect(FXORBDEATH);
orb[which].alive = FALSE;
wormscore(thissy - FIRSTHEAD, orb[which].score);
if (worm[thissy - FIRSTHEAD].bias)
{ worm[thissy - FIRSTHEAD].lives += ORBBLOOD;
stat(thissy - FIRSTHEAD, LIVESLINE);
} } }
else
{ effect(FXUSEARMOUR);
effect(FXORBDEATH);
orb[which].alive = FALSE;
wormscore(thissy - FIRSTHEAD, orb[which].score);
if (worm[thissy - FIRSTHEAD].bias)
{ worm[thissy - FIRSTHEAD].lives += ORBBLOOD;
stat(thissy - FIRSTHEAD, LIVESLINE);
} }
orb[which].last = thissy - FIRSTHEAD + FIRSTTAIL; // note sign issues
} else if (thissy >= FIRSTLETTER && thissy <= LASTLETTER)
{ score = ORBLETTER;
putletter(-1);
} else if (thissy >= FIRSTMISSILE && thissy <= LASTMISSILE)
{ missile[thissy - FIRSTMISSILE].alive = FALSE;
if (orb[which].mode != ARMOUR)
{ effect(FXORBDEATH);
wormscore(thissy - FIRSTMISSILE, orb[which].score);
if (worm[thissy - FIRSTMISSILE].bias)
{ worm[thissy - FIRSTMISSILE].lives += ORBBLOOD;
stat(thissy - FIRSTMISSILE, LIVESLINE);
}
orb[which].alive = FALSE;
draw(x, y, BONUS);
field[x][y] = BONUS;
} else effect(FXUSEARMOUR);
} else switch (thissy) {
case EMPTY:
score = EMPTYPOINT;
break;
case SILVER:
score = SILVERPOINT;
break;
case GOLD:
score = GOLDPOINT;
break;
case TELEPORT:
iwhich = whichteleport(x, y);
if (blocked(iwhich, orb[which].deltax, orb[which].deltay))
orb[which].alive = FALSE;
else
{ effect(FXUSETELEPORT);
score = TELPOINT;
orb[which].x = xwrap(teleport[level][partner(iwhich)].x + orb[which].deltax);
orb[which].y = ywrap(teleport[level][partner(iwhich)].y + orb[which].deltay);
}
break;
case ORB:
for (iwhich = 0; iwhich <= ORBS; iwhich++)
if (iwhich != which && orb[iwhich].alive && x == orb[iwhich].x && y == orb[iwhich].y)
{ effect(FXORBDEATH);
if (orb[iwhich].mode != ARMOUR)
{ orb[iwhich].alive = FALSE;
if (orb[which].mode != ARMOUR)
orb[which].alive = FALSE;
else effect(FXUSEARMOUR);
} else
{ orb[which].alive = FALSE;
if (orb[which].mode == ARMOUR)
orb[iwhich].alive = FALSE;
else effect(FXUSEARMOUR);
}
if (orb[which].alive && !orb[iwhich].alive)
score = orb[iwhich].score;
else if (!orb[which].alive && orb[iwhich].alive)
orb[iwhich].score += orb[which].score * orb[iwhich].multi;
else if (!orb[which].alive && !orb[iwhich].alive)
{ draw(x, y, BONUS);
field[x][y] = BONUS;
} }
break;
case FRAGMENT:
whichfrag(x, y, &iwhich, &iiwhich);
frag[iwhich][iiwhich].alive = FALSE;
if (orb[which].mode != ARMOUR)
orb[which].explode = TRUE;
else effect(FXUSEARMOUR);
break;
case SKULL:
effect(FXGETSKULL);
score = SKULLPOINT;
break;
case SLIME:
if (orb[which].mode != TONGUE)
orb[which].alive = FALSE;
break;
default:
break;
}
orb[which].score += score * orb[which].multi;
}
// update field
if (orb[which].alive)
{ if (orb[which].mode == TONGUE)
draw(orb[which].x, orb[which].y, ORBTONGUE);
else if (orb[which].mode == ARMOUR)
draw(orb[which].x, orb[which].y, ORBARMOUR);
else
draw(orb[which].x, orb[which].y, ORB);
field[orb[which].x][orb[which].y] = ORB;
}
}
void orbsplit(SBYTE which)
{ ABOOL available;
SBYTE copy = 0, iwhich, iiwhich;
effect(FXORBSPLIT);
for (iwhich = 0; iwhich <= ORBS; iwhich++)
{ available = TRUE;
for (iiwhich = 0; iiwhich <= 7; iiwhich++)
if (frag[iwhich][iiwhich].alive)
available = FALSE;
if (!orb[iwhich].alive && copy <= 3 && available)
{ orb[iwhich].x = orb[which].x;
orb[iwhich].y = orb[which].y;
orb[iwhich].score = orb[which].score;
orb[iwhich].armour = orb[which].armour;
orb[iwhich].tongue = orb[which].tongue;
orb[iwhich].mode = orb[which].mode;
orb[iwhich].speed = orb[which].speed;
orb[iwhich].explode = FALSE;
orb[iwhich].multi = orb[which].multi;
orb[iwhich].moved = FALSE;
switch (copy)
{
case 0:
if (orb[which].deltax != -1 || orb[which].deltay != -1)
{ orb[iwhich].deltax = -1;
orb[iwhich].deltay = -1;
orb[iwhich].alive = TRUE;
}
break;
case 1:
if (orb[which].deltax != 1 || orb[which].deltay != 1)
{ orb[iwhich].deltax = 1;
orb[iwhich].deltay = 1;
orb[iwhich].alive = TRUE;
}
break;
case 2:
if (orb[which].deltax != 1 || orb[which].deltay != -1)
{ orb[iwhich].deltax = 1;
orb[iwhich].deltay = -1;
orb[iwhich].alive = TRUE;
}
break;
case 3:
if (orb[which].deltax != -1 || orb[which].deltay != 1)
{ orb[iwhich].deltax = -1;
orb[iwhich].deltay = 1;
orb[iwhich].alive = TRUE;
}
break;
default:
break;
}
copy++;
} } }
void orientworms(void)
{ SBYTE player;
for (player = lo; player <= hi; player++)
{ worm[player].speed = SLOW;
if (worm[player].lives)
stat(player, SPEEDLINE);
worm[player].moved = FALSE;
worm[player].x = startx[level];
worm[player].y = starty[level];
switch (player)
{
case 0:
worm[0].deltax = -1;
worm[0].deltay = 0;
break;
case 1:
worm[1].deltax = 1;
worm[1].deltay = 0;
break;
case 2:
worm[2].deltax = 0;
worm[2].deltay = -1;
break;
case 3:
worm[3].deltax = 0;
worm[3].deltay = 1;
break;
default:
break;
} } }
SBYTE partner(SBYTE which)
{ if (which % 2 == 0)
return((SBYTE) (which + 1));
else return((SBYTE) (which - 1));
}
/* NAME putletter -- Put a letter onto the field
SYNOPSIS void putletter(SBYTE player);
INPUTS SBYTE player -
0-3: player on whose behalf the letter is put on for
-1: any letter
RESULT none
NOTES noletter -
-2: if we found somewhere to put the letter
player: if we didn't */
void putletter(SBYTE player)
{ SBYTE count = 0, letter;
SBYTE i, x, y;
do
{ x = rand() % (FIELDX + 1);
y = rand() % (FIELDY + 1);
} while ((field[x][y] < FIRSTEMPTY || field[x][y] > LASTEMPTY) && (++count < PATIENCE));
if (count < PATIENCE)
{ noletter = -2;
if (player != -1)
{ for (i = 0; i <= LETTERS; i++)
if (!(letters[player][i]))
{ break;
}
if (i > LETTERS)
letter = rand() % (LETTERS + 1);
else
{ do
letter = rand() % (LETTERS + 1);
while (letters[player][letter]);
}
} else
letter = rand() % (LETTERS + 1);
field[x][y] = letter + FIRSTLETTER;
draw(x, y, letter + FIRSTLETTER);
} else noletter = player;
}
/* NAME queue -- adds a keystroke to the key queue
SYNOPSIS name(SBYTE, SBYTE, SBYTE);
FUNCTION Adds a keystroke to the in-game key queue.
INPUTS player - player that pressed the key
deltax - the deltax of the key
deltay - the deltay of the key
IMPLEMENTATION
thequeue[] is an array, with QUEUELIMIT as its last index.
It is implemented as a FIFO stack rather than LIFO so that
the keystrokes are processed in the correct order (that is,
the order in which they were pressed). The oldest keystroke
is always at index [0], the next oldest at [1], and so on
upwards to the newest keystroke, at [worm[player].pos].
Keystrokes are removed from the bottom of the array ([0]),
and the rest of the array is shuffled down to fill the gap,
so that the contents of [1] go to [0], the contents of [2]
go to [1], etc. worm[player].pos is adjusted to always point
to the newest entry, which is the 'end' of the queue.
MODULE engine.c */
void queue(SBYTE player, SBYTE deltax, SBYTE deltay)
{ if (worm[player].pos < QUEUELIMIT)
{ worm[player].pos++;
thequeue[player][worm[player].pos].deltax = deltax;
thequeue[player][worm[player].pos].deltay = deltay;
} }
void reflect(SBYTE which, SBYTE iwhich)
{ frag[which][iwhich].deltax = -frag[which][iwhich].deltax;
frag[which][iwhich].deltay = -frag[which][iwhich].deltay;
}
void renderboard(void)
{ SBYTE i, x, y;
timeronoff(FALSE);
switch(rand() % 3) {
case 0:
// chequered
for (y = 0; y <= FIELDY; y += 2)
for (x = 0; x <= FIELDX; x += 2)
draw(x, y, board[level][x][y]);
for (x = 1; x <= FIELDX; x += 2)
for (y = 1; y <= FIELDY; y += 2)
draw(x, y, board[level][x][y]);
for (y = 0; y <= FIELDY; y += 2)
for (x = 1; x <= FIELDX; x += 2)
draw(x, y, board[level][x][y]);
for (x = 0; x <= FIELDX; x += 2)
for (y = 1; y <= FIELDY; y += 2)
draw(x, y, board[level][x][y]);
break;
case 1:
// `closing sliding doors'
for (x = 0; x <= FIELDX / 2; x++)
for (y = 0; y <= FIELDY; y++)
{ draw(x, y, board[level][x][y]);
draw(FIELDX - x, y, board[level][x][y]);
}
break;
case 2:
// triangle
for (i = 0; i <= FIELDY; i++)
for (y = 0; y <= i; y++)
{ draw(i, y, board[level][i][y]);
draw(y, i, board[level][y][i]);
}
for (i = FIELDY + 1; i <= FIELDX; i++)
for (y = 0; y <= FIELDY; y++)
draw(i, y, board[level][i][y]);
break;
default:
break;
}
timeronoff(TRUE);
if (a == FIELDEDIT)
{ draw(startx[level], starty[level], START);
if (teleport[level][0].alive)
{ draw(teleport[level][0].x, teleport[level][0].y, ONE);
draw(teleport[level][1].x, teleport[level][1].y, TWO);
} } }
ABOOL savefields(STRPTR fieldname)
{ SBYTE i, j;
TEXT IOBuffer[NAMELENGTH + 1];
matchteleports();
if (!ZOpen(fieldname, TRUE))
return FALSE;
// write header
strcpy(IOBuffer, "FSET 4.1");
IOBuffer[9] = levels;
if (!ZWrite(IOBuffer, 10))
{ ZClose();
return FALSE;
}
// write high score table
for (i = 0; i <= HISCORES; i++)
{ IOBuffer[0] = hiscore[i].player;
IOBuffer[1] = hiscore[i].level;
IOBuffer[2] = 0;
IOBuffer[3] = hiscore[i].score / 65536;
IOBuffer[4] = (hiscore[i].score % 65536) / 256;
IOBuffer[5] = (hiscore[i].score % 65536) % 256;
if (!ZWrite(IOBuffer, 6))
{ ZClose();
return FALSE;
}
for (j = 0; j <= NAMELENGTH; j++)
IOBuffer[j] = hiscore[i].name[j];
if (!ZWrite(IOBuffer, NAMELENGTH + 1))
{ ZClose();
return FALSE;
} }
// write level data
for (i = 0; i <= levels; i++)
{ IOBuffer[0] = startx[i];
IOBuffer[1] = starty[i];
IOBuffer[2] = teleport[i][0].alive;
IOBuffer[3] = teleport[i][0].x;
IOBuffer[4] = teleport[i][0].y;
IOBuffer[5] = teleport[i][1].alive;
IOBuffer[6] = teleport[i][1].x;
IOBuffer[7] = teleport[i][1].y;
if (!ZWrite(IOBuffer, 8))
{ ZClose();
return FALSE;
}
if (!ZWrite((char *) &board[i][0][0], (FIELDX + 1) * (FIELDY + 1)))
{ ZClose();
return FALSE;
} }
// write version string
if (!ZWrite(VERSION, strlen(VERSION)))
{ ZClose();
return FALSE;
}
ZClose();
if (clearthem)
clearhiscores();
modified = FALSE;
return TRUE;
}
void saylevel(COLOUR colour)
{ TEXT mainstring[15] = "Level ", tempstring[4];
if (level > 0)
{ stci_d(&mainstring[6], level);
strcat((char*) mainstring, " of ");
stci_d(tempstring, levels);
strcat((char*) mainstring, (char*) tempstring);
say(mainstring, colour);
} else say((STRPTR) "Treasury", colour);
}
void setbrush(SBYTE newbrush)
{ brush = newbrush;
setpointer(brush);
underline(brush);
}
SBYTE slowdown(SBYTE speed, ABOOL nitro)
{ speed *= 2;
if (nitro)
{ if (speed > VERYSLOW)
speed = VERYSLOW;
} else if (speed > SLOW)
speed = SLOW;
return(speed);
}
/* NAME slowloop -- things done rarely
SYNOPSIS slowloop(void);
FUNCTION Decrements strength; creates killers, orbs, objects,
teleports, slime; controls timebombs; puts letters if
neccessary; blanks out old causes.
MODULE engine.c */
void slowloop(void)
{ ABOOL available, done;
SBYTE i, iwhich, player, which, x, xx, y, yy;
// decrement strength
for (player = lo; player <= hi; player++)
if (worm[player].lives > 0 && ice == -1 || ice == player)
{ if (worm[player].bias > 0)
{ worm[player].bias--;
stat(player, BIASLINE);
}
if (worm[player].ice > 0 && --worm[player].ice == 0)
{ for (which = lo; which <= hi; which++)
if (player != which)
worm[player].pos = -1;
ice = -1;
}
if (worm[player].mode == ARMOUR)
{ if (--worm[player].armour == 0)
{ if (worm[player].tongue > 0)
worm[player].mode = TONGUE;
else worm[player].mode = NULL;
}
stat(player, ARMOURLINE);
} else if (worm[player].mode == TONGUE)
{ if (--worm[player].tongue == 0)
{ if (worm[player].armour > 0)
worm[player].mode = ARMOUR;
else worm[player].mode = NULL;
}
stat(player, TONGUELINE);
} }
if (ice == -1)
{ for (which = 0; which <= ORBS; which++)
if (orb[which].alive)
if (orb[which].mode == ARMOUR)
{ if (--orb[which].armour == 0)
if (orb[which].tongue > 0)
orb[which].mode = TONGUE;
else orb[which].mode = NULL;
} else if (orb[which].mode == TONGUE)
{ if (--orb[which].tongue == 0)
if (orb[which].armour > 0)
orb[which].mode = ARMOUR;
else orb[which].mode = NULL;
}
// create killers
for (which = 0; which <= KILLERS; which++)
if (!killer[which].alive && !(rand() % killerfreq) && findempty(&x, &y, FIRSTKILLER, LASTKILLER) && field[x][y] != KILLER)
{ effect(FXKILLERBORN);
killer[which].x = x;
killer[which].y = y;
killer[which].alive = TRUE;
killer[which].last = field[killer[which].x][killer[which].y];
field[killer[which].x][killer[which].y] = KILLER;
draw(killer[which].x, killer[which].y, KILLER);
}
// create orbs
for (which = 0; which <= ORBS; which++)
if (!orb[which].alive && !(rand() % orbfreq))
{ orb[which].x = rand() % (FIELDX + 1);
orb[which].y = rand() % (FIELDY + 1);
available = TRUE;
for (iwhich = 0; iwhich <= 7; iwhich++)
if (frag[which][iwhich].alive)
available = FALSE;
if (available && field[orb[which].x][orb[which].y] >= FIRSTEMPTY && field[orb[which].x][orb[which].y] <= LASTEMPTY)
{ orb[which].deltax = (rand() % 2) * 2 - 1;
orb[which].deltay = (rand() % 2) * 2 - 1;
if (field[orb[which].x + orb[which].deltay][orb[which].y + orb[which].deltay] >= FIRSTEMPTY && field[orb[which].x + orb[which].deltax][orb[which].y + orb[which].deltay] <= LASTEMPTY)
{ effect(FXORBBORN);
orb[which].score = 0;
orb[which].alive = TRUE;
orb[which].armour = 0;
orb[which].tongue = 0;
orb[which].mode = NULL;
orb[which].speed = orbspeed;
orb[which].explode = FALSE;
orb[which].multi = 1;
orb[which].moved = FALSE;
} } }
// create objects
for (which = 0; which <= LASTOBJECT; which++)
if (!(rand() % object[which].frequency) && findempty(&x, &y, FIRSTEMPTY, LASTEMPTY))
{ field[x][y] = which;
draw(x, y, which);
}
// create teleports
if (!(rand() % TELFREQ)
&& !teleport[level][2].alive
&& findempty(&(teleport[level][2].x), &(teleport[level][2].y), FIRSTEMPTY, LASTEMPTY)
&& findempty(&(teleport[level][3].x), &(teleport[level][3].y), FIRSTEMPTY, LASTEMPTY)
&& (teleport[level][2].x != teleport[level][3].x || teleport[level][2].y != teleport[level][3].y))
{ teleport[level][2].alive = TRUE;
field[teleport[level][2].x][teleport[level][2].y] = TELEPORT;
draw(teleport[level][2].x, teleport[level][2].y, TELEPORT);
teleport[level][3].alive = TRUE;
field[teleport[level][3].x][teleport[level][3].y] = TELEPORT;
draw(teleport[level][3].x, teleport[level][3].y, TELEPORT);
}
// create timebombs
for (i = 0; i <= level; i++)
{ x = rand() % (FIELDX + 1);
y = rand() % (FIELDY + 1);
if (field[x][y] == BOMB)
{ done = FALSE;
for (which = 0; which <= TIMEBOMBS; which++)
if (timebomb[which].alive == FALSE && !done)
{ timebomb[which].alive = TRUE;
timebomb[which].x = x;
timebomb[which].y = y;
timebomb[which].time = 10;
done = TRUE;
} } }
// decrement and explode timebombs
if (!(r % (VERYSLOW * TIMEBOMBSPEED)))
for (which = 0; which <= TIMEBOMBS; which++)
if (timebomb[which].alive)
{ if (field[timebomb[which].x][timebomb[which].y] != BOMB)
timebomb[which].alive = FALSE;
else
{ effect(FXTIMEBOMBTICK);
timebomb[which].time--;
if (timebomb[which].time < 0)
{ timebomb[which].alive = FALSE;
field[timebomb[which].x][timebomb[which].y] = EMPTY;
draw(timebomb[which].x, timebomb[which].y, EMPTY);
bombblast(BOMB, 0, timebomb[which].x, timebomb[which].y);
} else draw(timebomb[which].x, timebomb[which].y, ZERO + timebomb[which].time);
} }
// create slime
if (!(rand() % slimefreq))
{ if (findempty(&x, &y, FIRSTEMPTY, LASTEMPTY))
{ field[x][y] = SLIME;
draw(x, y, SLIME);
} }
// grow slime
if (!(rand() % slimegrowfreq))
{ for (x = 0; x <= FIELDX; x++)
for (y = 0; y <= FIELDY; y++)
if (field[x][y] == SLIME)
for (xx = x - 1; xx <= x + 1; xx++)
for (yy = y - 1; yy <= y + 1; yy++)
if (valid(xx, yy) && field[xx][yy] == EMPTY && !(rand() % 2))
field[xx][yy] = TEMPSLIME;
for (x = 0; x <= FIELDX; x++)
for (y = 0; y <= FIELDY; y++)
if (field[x][y] == TEMPSLIME)
{ field[x][y] = SLIME;
draw(x, y, SLIME);
} }
// retry putting letters if necessary
if (noletter != -2)
putletter(noletter);
}
// blank out old causes
for (player = lo; player <= hi; player++)
{ if (worm[player].lives > 0 && r > worm[player].causewait)
{ drawcause(player, BLACK);
worm[player].causewait = (ULONG) -1; // most future time possible
} }
}
SBYTE speedup(SBYTE speed, ABOOL nitro)
{ speed /= 2;
if (nitro)
{ if (speed < VERYFAST)
speed = VERYFAST;
} else if (speed < FAST)
speed = FAST;
return(speed);
}
LONG squareblast(SBYTE type, SBYTE player, SBYTE thissy, SBYTE x, SBYTE y)
{ SBYTE iwhich, which;
LONG score = 0L;
if (thissy <= LASTOBJECT)
{ field[x][y] = EMPTY;
draw(x, y, EMPTY);
if (thissy == BOMB)
for (which = 0; which <= TIMEBOMBS; which++)
if (timebomb[which].alive && timebomb[which].x == x && timebomb[which].y == y)
timebomb[which].alive = FALSE;
} else if (thissy >= FIRSTTAIL) // assumes && thissy <= LASTTAIL
{ draw(x, y, EMPTY);
field[x][y] = EMPTY;
} else if (thissy == ORB)
{ which = whichorb(x, y);
if (orb[which].mode != ARMOUR)
{ effect(FXORBDEATH);
orb[which].alive = FALSE;
score = orb[which].score;
if (type == HEAD && worm[player].bias)
worm[player].lives += ORBBLOOD;
draw(x, y, BONUS);
field[x][y] = BONUS;
} else effect(FXUSEARMOUR);
} else if (thissy >= FIRSTHEAD && thissy <= LASTHEAD)
{ if (type != HEAD || player != thissy - FIRSTHEAD)
if (worm[thissy - FIRSTHEAD].mode != ARMOUR)
{ worm[thissy - FIRSTHEAD].cause = BOMB;
worm[thissy - FIRSTHEAD].alive = FALSE;
stat(thissy - FIRSTHEAD, LIVESLINE);
if (type == HEAD)
worm[thissy - FIRSTHEAD].victor = player;
else
{ worm[thissy - FIRSTHEAD].victor = -1;
score = KILLWORM; // worms will get thissy bonus from death(), so it is not given for them here.
}
} else effect(FXUSEARMOUR);
} else if (thissy >= FIRSTMISSILE && thissy <= LASTMISSILE)
{ if (type != HEAD || player != thissy - FIRSTMISSILE)
{ stopfx(missile[thissy - FIRSTMISSILE].receipt);
missile[thissy - FIRSTMISSILE].alive = FALSE;
draw(x, y, EMPTY);
field[x][y] = EMPTY;
} }
else if (thissy == FRAGMENT)
{ whichfrag(x, y, &which, &iwhich);
frag[which][iwhich].alive = FALSE;
draw(x, y, EMPTY);
field[x][y] = EMPTY;
} else if (thissy == KILLER)
{ which = whichkiller(x, y);
killer[which].alive = FALSE;
draw(x, y, BONUS);
field[x][y] = BONUS;
}
return(score);
}
void timeloop(void)
{
TEXT timedisplay[5] = {"#:##"};
if (secondsleft < 0L)
secondsleft = 0L;
timedisplay[0] = 48 + (secondsleft / 60);
timedisplay[2] = 48 + ((secondsleft % 60) / 10);
timedisplay[3] = 48 + ((secondsleft % 60) % 10);
if (!level)
{ say(timedisplay, worm[treasurer].colour);
if (!secondsleft)
{ level = reallevel + 1;
secondsleft = SECONDSPERLEVEL;
newlevel(treasurer);
} }
else if (!secondsleft)
{ if (outoftime == 1)
{ fragspeed = speedup(fragspeed, TRUE);
orbspeed = speedup(orbspeed, TRUE);
killerspeed = speedup(killerspeed, TRUE);
killerfreq /= 2;
if (killerfreq < KILLERFREQEND)
killerfreq = KILLERFREQEND;
orbfreq /= 2;
if (orbfreq < ORBFREQEND)
orbfreq = ORBFREQEND;
slimefreq /= 2;
if (slimefreq < SLIMEFREQEND)
slimefreq = SLIMEFREQEND;
slimegrowfreq /= 2;
if (slimegrowfreq < SLIMEGROWFREQEND)
slimegrowfreq = SLIMEGROWFREQEND;
outoftime = 2;
}
say(timedisplay, LIGHTGREY);
} else if (secondsleft <= 10)
{ if (outoftime == 0)
{ effect(FXTIMEALERT);
outoftime = 1;
}
say(timedisplay, RED);
} else if (secondsleft <= 20)
say(timedisplay, YELLOW);
else say(timedisplay, GREEN);
}
void train(SCANCODE scancode)
{ SBYTE i, x, y;
switch(scancode) {
case HELP:
if (!trainer)
trainer = TRUE;
else trainer = FALSE;
break;
case NUMERICMINUS:
// Fill most squares with empty.
if (trainer)
{ trainer = FALSE;
for (x = 0; x <= FIELDX; x++)
for (y = 0; y <= FIELDY; y++)
if (field[x][y] != STONE && field[x][y] != KILLER && field[x][y] != ORB && field[x][y] != SKULL && (field[x][y] < FIRSTHEAD || field[x][y] > LASTHEAD) && (field[x][y] < FIRSTLETTER || field[x][y] > LASTLETTER))
{ draw(x, y, EMPTY);
field[x][y] = EMPTY;
} }
break;
case NUMERICSLASH:
// Complete the level.
if (trainer)
{ trainer = FALSE;
if (worm[1].lives > 0)
for (i = 0; i <= LETTERS; i++)
letters[1][i] = TRUE;
}
break;
case NUMERICASTERISK:
// Change most squares to gold.
if (trainer)
{ trainer = FALSE;
for (x = 0; x <= FIELDX; x++)
for (y = 0; y <= FIELDY; y++)
if (field[x][y] != STONE && field[x][y] != KILLER && field[x][y] != ORB && field[x][y] != SKULL && (field[x][y] < FIRSTHEAD || field[x][y] > LASTHEAD) && (field[x][y] < FIRSTLETTER || field[x][y] > LASTLETTER))
{ draw(x, y, GOLD);
field[x][y] = GOLD;
} }
break;
case NUMERICPLUS:
// Full lives, tongue, bias, ammo, power, nitro and affixer.
if (trainer)
{ trainer = FALSE;
if (worm[1].lives > 0)
{ worm[1].lives = LIVESLIMIT;
stat(1, LIVESLINE);
worm[1].tongue = MODELIMIT;
worm[1].mode = TONGUE;
stat(1, TONGUELINE);
worm[1].bias = BIASLIMIT;
stat(1, BIASLINE);
worm[1].ammo = AMMOLIMIT;
stat(1, AMMOLINE);
worm[1].power = POWERLIMIT;
stat(1, POWERLINE);
worm[1].nitro = TRUE;
stat(1, SPEEDLINE);
worm[1].affixer = TRUE;
trainer = FALSE;
} }
break;
default:
break;
} }
void turnworm(SBYTE player, SBYTE deltax, SBYTE deltay)
{
if (worm[player].nitro || !deltax || !deltay)
{ if (worm[player].deltax == deltax && worm[player].deltay == deltay)
{ worm[player].speed = speedup(worm[player].speed, worm[player].nitro);
stat(player, SPEEDLINE);
} else if (worm[player].deltax == -deltax && worm[player].deltay == -deltay)
{ worm[player].speed = slowdown(worm[player].speed, worm[player].nitro);
stat(player, SPEEDLINE);
} else
{ worm[player].deltax = deltax;
worm[player].deltay = deltay;
} }
}
void updatesquare(SBYTE x, SBYTE y)
{ SBYTE which;
if (startx[level] == x && starty[level] == y)
draw(x, y, START);
else if (board[level][x][y] == TELEPORT)
{ for (which = 0; which <= 1; which++)
if (teleport[level][which].alive && teleport[level][which].x == x && teleport[level][which].y == y)
draw(x, y, ONE + which);
} else draw(x, y, board[level][x][y]);
}
SBYTE valid(SBYTE x, SBYTE y)
{ if (x >= 0 && x <= FIELDX && y >= 0 && y <= FIELDY)
return(TRUE);
else
return(FALSE);
}
void whichfrag(SBYTE x, SBYTE y, SBYTE* whichptr, SBYTE* iwhichptr)
{ SBYTE which, iwhich;
for (which = 0; which <= ORBS + 1; which++)
for (iwhich = 0; iwhich <= 7; iwhich++)
if (frag[which][iwhich].alive && frag[which][iwhich].x == x && frag[which][iwhich].y == y)
{ *whichptr = which;
*iwhichptr = iwhich;
return;
} }
SBYTE whichkiller(SBYTE x, SBYTE y)
{ SBYTE which;
for (which = 0; which <= KILLERS; which++)
if (killer[which].alive && killer[which].x == x && killer[which].y == y)
return(which);
return(-1); // error code
}
SBYTE whichorb(SBYTE x, SBYTE y)
{ SBYTE which;
for (which = 0; which <= ORBS; which++)
if (orb[which].alive && orb[which].x == x && orb[which].y == y)
return(which);
return(-1); // error code
}
SBYTE whichteleport(SBYTE x, SBYTE y)
{ SBYTE which;
for (which = 0; which <= TIMEBOMBS; which++)
if (teleport[level][which].alive && teleport[level][which].x == x && teleport[level][which].y == y)
return((SBYTE) which);
return((SBYTE) -1); // error code
}
SBYTE whichtimebomb(SBYTE x, SBYTE y)
{ SBYTE which;
for (which = 0; which <= TIMEBOMBS; which++)
if (timebomb[which].alive && timebomb[which].x == x && timebomb[which].y == y)
return(which);
return(-1); // error code
}
void wormbullet(SBYTE player)
{ ABOOL finished,
flag,
ok = FALSE,
lettered = FALSE;
LONG score;
SBYTE distance,
thissy,
i, j, k,
x, y;
if (!worm[player].ammo)
{ stat(player, SCORELINE);
if (worm[player].speed == FAST)
distance = FASTDISTANCE;
else if (worm[player].speed == NORMAL)
distance = NORMALDISTANCE;
else if (worm[player].speed == SLOW)
distance = SLOWDISTANCE;
else if (worm[player].speed == VERYFAST)
distance = VERYFASTDISTANCE;
else distance = VERYSLOWDISTANCE;
if (abs(worm[player].deltax) <= 1 && abs(worm[player].deltay) <= 1)
{ x = xwrap(worm[player].x + worm[player].deltax * distance);
y = ywrap(worm[player].y + worm[player].deltay * distance);
thissy = field[x][y];
if (thissy == TELEPORT)
{ i = whichteleport(x, y);
if (!blocked(i, worm[player].deltax, worm[player].deltay))
ok = TRUE;
}
if (ok || thissy < STONE || thissy > KILLER)
{ flag = FALSE;
for (i = lo; i <= hi; i++)
if (worm[i].control == HUMAN)
flag = TRUE;
if (!flag || worm[player].control == HUMAN)
effect(FXJUMP);
// Amiga-worms only make jumping sounds in demo mode
worm[player].deltax *= distance;
worm[player].deltay *= distance;
} } }
else
{ effect(FXSHOOT);
worm[player].ammo--;
stat(player, AMMOLINE);
if (!missile[player].alive && worm[player].bias)
{ missile[player].receipt = effect(FXMISSILEACTIVE);
missile[player].alive = TRUE;
missile[player].x = worm[player].x;
missile[player].y = worm[player].y;
missile[player].moved = FALSE;
}
for (i = 0; i <= worm[player].power; i++)
{ bullet[i].alive = TRUE;
bullet[i].teleported = 0;
bullet[i].visible = TRUE;
if (i % 2 == 0)
distance = i / 2;
else
distance = -((i + 1) / 2);
if (worm[player].deltax == 0)
{ bullet[i].x = worm[player].x + distance;
bullet[i].y = worm[player].y + worm[player].deltay;
} else if (worm[player].deltay == 0)
{ bullet[i].x = worm[player].x + worm[player].deltay;
bullet[i].y = worm[player].y + distance;
} else
{ switch (i) {
case 0:
bullet[i].x = worm[player].x + worm[player].deltax;
bullet[i].y = worm[player].y + worm[player].deltay;
break;
case 1:
bullet[i].x = worm[player].x + worm[player].deltax;
bullet[i].y = worm[player].y;
break;
case 2:
bullet[i].x = worm[player].x;
bullet[i].y = worm[player].y + worm[player].deltay;
break;
case 3:
bullet[i].x = worm[player].x + worm[player].deltax * 2;
bullet[i].y = worm[player].y;
break;
case 4:
bullet[i].x = worm[player].x;
bullet[i].y = worm[player].y + worm[player].deltay * 2;
break;
case 5:
bullet[i].x = worm[player].x + worm[player].deltax * 2;
bullet[i].y = worm[player].y - worm[player].deltay;
break;
case 6:
bullet[i].x = worm[player].x - worm[player].deltax;
bullet[i].y = worm[player].y + worm[player].deltay * 2;
break;
default:
break;
} }
if (!(valid(bullet[i].x, bullet[i].y)))
bullet[i].alive = FALSE;
}
score = 0L;
finished = FALSE;
while (!finished)
{ finished = TRUE;
for (i = 0; i <= worm[player].power; i++)
{ if (bullet[i].alive)
{ finished = FALSE;
if (bullet[i].visible)
if (bullet[i].teleported)
{ if (worm[player].bias)
{ draw(bullet[i].x, bullet[i].y, GOLD);
field[bullet[i].x][bullet[i].y] = GOLD;
} else
{ draw(bullet[i].x, bullet[i].y, SILVER);
field[bullet[i].x][bullet[i].y] = SILVER;
} }
else
{ draw(bullet[i].x, bullet[i].y, EMPTY);
field[bullet[i].x][bullet[i].y] = EMPTY;
}
else bullet[i].visible = TRUE;
bullet[i].x += worm[player].deltax;
bullet[i].y += worm[player].deltay;
if (!(valid(bullet[i].x, bullet[i].y)))
bullet[i].alive = FALSE;
else
{ thissy = field[bullet[i].x][bullet[i].y];
if (thissy >= FIRSTHEAD && thissy <= LASTHEAD)
{ if (worm[thissy - FIRSTHEAD].mode != ARMOUR)
{ worm[thissy - FIRSTHEAD].cause = FIRSTFIRE + player;
worm[thissy - FIRSTHEAD].victor = player;
worm[thissy - FIRSTHEAD].alive = FALSE;
score += KILLWORM + HITSHOT;
} else effect(FXUSEARMOUR);
bullet[i].alive = FALSE;
} else if (thissy >= FIRSTPROTECTOR && thissy <= LASTPROTECTOR)
{ if (player != thissy - FIRSTPROTECTOR)
{ effect(FXUSEPROTECTOR);
bullet[i].alive = FALSE;
} else
bullet[i].visible = FALSE;
} else if (thissy >= FIRSTMISSILE && thissy <= LASTMISSILE)
{ if (player != thissy - FIRSTMISSILE)
{ stopfx(missile[thissy - FIRSTMISSILE].receipt);
missile[thissy - FIRSTMISSILE].alive = FALSE;
} else bullet[i].visible = FALSE;
} else if (thissy >= FIRSTLETTER && thissy <= LASTLETTER)
{ wormletter(player, thissy);
lettered = TRUE;
} else
{ switch(thissy) {
case STONE:
bullet[i].alive = FALSE;
break;
case ORB:
for (j = 0; j <= ORBS; j++)
if (orb[j].alive && orb[j].x == bullet[i].x && orb[j].y == bullet[i].y)
{ bullet[i].alive = FALSE;
if (orb[j].mode != ARMOUR)
{ orb[j].explode = TRUE;
score += orb[j].score + HITSHOT;
if (worm[player].bias)
worm[player].lives += ORBBLOOD;
} else effect(FXUSEARMOUR);
}
break;
case TELEPORT:
j = whichteleport(bullet[i].x, bullet[i].y);
if (bullet[i].teleported == 2)
bullet[i].alive = FALSE;
else
{ effect(FXUSETELEPORT);
bullet[i].visible = FALSE;
bullet[i].teleported++;
bullet[i].x = teleport[level][partner(j)].x;
bullet[i].y = teleport[level][partner(j)].y;
score += TELPOINT;
}
break;
case WOOD:
if (!worm[player].bias)
bullet[i].alive = FALSE;
break;
case KILLER:
for (j = 0; j <= KILLERS; j++)
if (killer[j].alive && killer[j].x == bullet[i].x && killer[j].y == bullet[i].y)
{ killer[j].alive = FALSE;
draw(bullet[i].x, bullet[i].y, BONUS);
field[bullet[i].x][bullet[i].y] = BONUS;
score += KILLKILLER + HITSHOT;
if (worm[player].bias)
worm[player].lives += KILLERBLOOD;
}
bullet[i].alive = FALSE;
break;
case SKULL:
bullet[i].alive = FALSE;
break;
case FRAGMENT:
bullet[i].alive = FALSE;
whichfrag(bullet[i].x, bullet[i].y, &j, &k);
frag[j][k].alive = FALSE;
field[bullet[i].x][bullet[i].y] = EMPTY;
draw(bullet[i].x, bullet[i].y, EMPTY);
break;
case BOMB:
j = whichtimebomb(bullet[i].x, bullet[i].y);
if (j != -1)
{ bullet[i].alive = FALSE;
timebomb[j].alive = FALSE;
field[timebomb[j].x][timebomb[j].y] = EMPTY;
draw(timebomb[j].x, timebomb[j].y, EMPTY);
bombblast(HEAD, player, timebomb[j].x, timebomb[j].y);
}
break;
default:
break;
}
if (bullet[i].alive && bullet[i].visible)
draw(bullet[i].x, bullet[i].y, FIRSTFIRE + player);
} } } } }
if (lettered)
putletter(player);
wormscore(player, score);
if (worm[player].bias)
stat(player, LIVESLINE);
clearkybd();
} }
void wormletter(SBYTE player, SBYTE thissy)
{ if (thissy == GREEN_C)
effect(FX_C);
else if (thissy == RED_O)
effect(FX_O);
else if (thissy == BLUE_M)
effect(FX_M);
else if (thissy == YELLOW_P)
effect(FX_P);
else if (thissy == GREEN_L)
effect(FX_L);
else if (thissy == BLUE_T)
effect(FX_T);
else // assumes RED_E or YELLOW_E
effect(FX_E);
letters[player][thissy - FIRSTLETTER] = TRUE;
drawletter(player, thissy, NORMAL);
if (player == (thissy - FIRSTLETTER) % 4)
wormscore(player, MYLETTER);
else wormscore(player, YOURLETTER);
}
/* NAME wormloop -- controls worms and protectors
SYNOPSIS wormloop(SBYTE);
FUNCTION Amiga-worm thinking, processing one keystroke from the
worm's queue, all the worm's protectors' control, the
worm's control.
MODULE engine.c */
void wormloop(SBYTE player)
{
ABOOL complete, done, flag;
SBYTE bestgood, bestx, besty, creature, died, dirx, diry, good, i, ithis,
iwhich, thissy, thisletter, thisprot = -1, which, x, xx, y, yy;
LONG score = 0L;
/* AI: Amiga worm control
Worm checks ahead, left and right one square. Assigns opinions to those
three choices and then takes the appropriate one.
Possible enhancements:
recognition of possession of objects
(esp. ammo, nitro, tongue, armour)
finer gradients of decision
(eg. certain objects better than others)
longer lookahead */
if (worm[player].control == AMIGA)
{ if (!(rand() % 200))
queue(player, (rand() % 3) - 1, (rand() % 3) - 1);
else
{ bestgood = -128;
for (i = 0; i <= 2; i++)
{ switch(i) {
case 0:
dirx = worm[player].deltax;
diry = worm[player].deltay;
break;
case 1:
if (worm[player].deltax == 0) // if going north or south
{ dirx = -1; // then look west
diry = 0;
} else // if going east or west
{ dirx = 0; // then look north
diry = -1;
}
break;
case 2:
if (worm[player].deltax == 0) // if going north or south
{ dirx = 1; // then look east
diry = 0;
} else // if going east or west
{ dirx = 0; // then look south
diry = 1;
}
break;
default:
break;
}
thissy = field[xwrap(worm[player].x + dirx)][ywrap(worm[player].y + diry)];
if (thissy >= FIRSTLETTER && thissy <= LASTLETTER)
good = 100;
else if (thissy <= LASTOBJECT)
good = 80;
else if (thissy >= FIRSTTAIL && thissy <= LASTTAIL)
{ if (player == thissy - FIRSTTAIL)
good = -30;
else good = -60;
} else switch(thissy) {
case SKULL:
good = 70;
break;
case GOLD:
good = 50;
break;
case SILVER:
good = 40;
break;
case EMPTY:
good = 10;
break;
case WOOD:
good = -80;
break;
case STONE:
good = -90;
break;
case KILLER:
good = -100;
break;
default:
good = -50; // slime, heads, orbs, etc.
break;
}
if (good > bestgood)
{ bestx = dirx;
besty = diry;
bestgood = good;
} }
if (bestgood <= -60)
queue(player, 0, 0);
else if (bestgood < 0 && (!(rand() % 2)))
queue(player, 0, 0);
// Amiga worms are not allowed to go VERYFAST
else if (bestx != worm[player].deltax || besty != worm[player].deltay || worm[player].speed > FAST)
queue(player, bestx, besty);
} }
// remove a keystroke from the queue
if (worm[player].pos != -1)
{ if (thequeue[player][0].deltax == 0 && thequeue[player][0].deltay == 0)
wormbullet(player);
else turnworm(player, thequeue[player][0].deltax, thequeue[player][0].deltay);
if (--worm[player].pos != -1)
for (i = 0; i <= worm[player].pos; i++)
{ thequeue[player][i].deltax = thequeue[player][i + 1].deltax;
thequeue[player][i].deltay = thequeue[player][i + 1].deltay;
} }
// move worm
field[worm[player].x][worm[player].y] = worm[player].last;
draw(worm[player].x, worm[player].y, worm[player].last);
worm[player].x = xwrap(worm[player].x + worm[player].deltax);
worm[player].y = ywrap(worm[player].y + worm[player].deltay);
worm[player].deltax = bsign(worm[player].deltax);
worm[player].deltay = bsign(worm[player].deltay);
worm[player].last = FIRSTTAIL + player;
// move protectors
for (which = 0; which <= PROTECTORS; which++)
{ if (protector[player][which].alive)
{ if (protector[player][which].visible)
{ draw(protector[player][which].x, protector[player][which].y, protector[player][which].last);
field[protector[player][which].x][protector[player][which].y] = protector[player][which].last;
} else protector[player][which].visible = TRUE;
protector[player][which].last = EMPTY;
if (which == NOSE)
{ protector[player][which].relx = worm[player].deltax * NOSEDISTANCE;
protector[player][which].rely = worm[player].deltay * NOSEDISTANCE;
if (!worm[player].affixer)
{ if (worm[player].position == -1)
worm[player].posidir = 1;
else if (worm[player].position == 1)
worm[player].posidir = -1;
worm[player].position += worm[player].posidir;
if (worm[player].deltax == 0)
protector[player][which].relx = worm[player].position;
else if (worm[player].deltay == 0)
protector[player][which].rely = worm[player].position;
else if (worm[player].position == -1)
protector[player][which].relx = worm[player].deltax * (NOSEDISTANCE - 1);
else if (worm[player].position == 1)
protector[player][which].rely = worm[player].deltay * (NOSEDISTANCE - 1);
} }
else if (!worm[player].affixer)
{ if (protector[player][which].relx == 1 && protector[player][which].rely == -1)
{ protector[player][which].deltax = 0;
protector[player][which].deltay = 1;
} else if (protector[player][which].relx == 1 && protector[player][which].rely == 1)
{ protector[player][which].deltax = -1;
protector[player][which].deltay = 0;
} else if (protector[player][which].relx == -1 && protector[player][which].rely == 1)
{ protector[player][which].deltax = 0;
protector[player][which].deltay = -1;
} else if (protector[player][which].relx == -1 && protector[player][which].rely == -1)
{ protector[player][which].deltax = 1;
protector[player][which].deltay = 0;
}
protector[player][which].relx += protector[player][which].deltax;
protector[player][which].rely += protector[player][which].deltay;
}
protector[player][which].x = worm[player].x + protector[player][which].relx;
protector[player][which].y = worm[player].y + protector[player][which].rely;
if (!valid(protector[player][which].x, protector[player][which].y))
protector[player][which].visible = FALSE;
} }
// collision detection
creature = HEAD;
while (creature != NULL)
{ if (creature == HEAD)
{ x = worm[player].x;
y = worm[player].y;
} else
{ x = protector[player][thisprot].x;
y = protector[player][thisprot].y;
}
thissy = field[x][y];
if (creature == HEAD)
{ if (thissy >= FIRSTHEAD && thissy <= LASTHEAD)
{ died = 0;
if (worm[thissy - FIRSTHEAD].mode != TONGUE)
{ worm[thissy - FIRSTHEAD].cause = FIRSTHEAD + player;
worm[thissy - FIRSTHEAD].alive = FALSE;
worm[thissy - FIRSTHEAD].victor = player;
died++;
}
if (worm[player].mode != TONGUE)
{ worm[player].cause = thissy;
worm[player].alive = FALSE;
worm[player].victor = thissy - FIRSTHEAD;
died++;
}
if (died == 0)
{ worm[thissy - FIRSTHEAD].score += CROSSHEADS * worm[thissy - FIRSTHEAD].multi;
score += CROSSHEADS;
} else if (died == 2)
{ worm[thissy - FIRSTHEAD].victor = -1;
worm[player].victor = -1;
} }
else if (thissy >= FIRSTTAIL && thissy <= LASTTAIL)
{ if (worm[player].mode == TONGUE)
{ if (!worm[player].tonguereceipt)
worm[player].tonguereceipt = effect(FXUSETONGUE);
if (lo != hi)
if (player == thissy - FIRSTTAIL)
{ score += TURNTOSILVER;
worm[player].last = SILVER;
} else
{ score += TURNTOGOLD;
worm[player].last = GOLD;
} }
else
{ worm[player].cause = thissy;
worm[player].alive = FALSE;
worm[player].victor = thissy - FIRSTTAIL;
} }
else if (thissy >= FIRSTPROTECTOR && thissy <= LASTPROTECTOR)
if (worm[player].mode != ARMOUR)
{ worm[player].cause = thissy;
worm[player].victor = thissy - FIRSTPROTECTOR;
worm[player].alive = FALSE;
} else effect(FXUSEARMOUR);
else if (thissy >= FIRSTMISSILE && thissy <= LASTMISSILE)
{ if (player != thissy - FIRSTMISSILE)
{ stopfx(missile[thissy - FIRSTMISSILE].receipt);
missile[thissy - FIRSTMISSILE].alive = FALSE;
if (worm[player].mode != ARMOUR)
{ worm[player].alive = FALSE;
worm[player].cause = thissy;
worm[player].victor = thissy - FIRSTMISSILE;
} else effect(FXUSEARMOUR);
} }
else if (thissy == STONE || thissy == KILLER)
{ worm[player].cause = thissy;
worm[player].victor = -1;
worm[player].alive = FALSE;
worm[player].x = xwrap(worm[player].x - worm[player].deltax);
worm[player].y = ywrap(worm[player].y - worm[player].deltay);
} else if (thissy == WOOD)
{ if (worm[player].mode != TONGUE)
{ worm[player].cause = WOOD;
worm[player].alive = FALSE;
worm[player].victor = -1;
} }
else if (thissy == SLIME)
{ if (worm[player].mode != ARMOUR)
{ worm[player].cause = SLIME;
worm[player].alive = FALSE;
worm[player].victor = -1;
} }
else if (thissy == TELEPORT)
{ which = whichteleport(x, y);
if (blocked(which, worm[player].deltax, worm[player].deltay))
{ worm[player].cause = TELEPORT;
worm[player].victor = -1;
worm[player].alive = FALSE;
worm[player].x = xwrap(worm[player].x - worm[player].deltax);
worm[player].y = ywrap(worm[player].y - worm[player].deltay);
} else
{ effect(FXUSETELEPORT);
score += TELPOINT;
worm[player].x = xwrap(teleport[level][partner(which)].x + worm[player].deltax);
worm[player].y = ywrap(teleport[level][partner(which)].y + worm[player].deltay);
} } }
else // creature == PROTECTOR
{ if (thissy >= FIRSTHEAD && thissy <= LASTHEAD)
{ if (player != thissy - FIRSTHEAD)
{ if (worm[thissy - FIRSTHEAD].mode != ARMOUR)
{ effect(FXUSEPROTECTOR);
worm[thissy - FIRSTHEAD].cause = FIRSTPROTECTOR + player;
worm[thissy - FIRSTHEAD].alive = FALSE;
worm[thissy - FIRSTHEAD].victor = player;
} else
{ effect(FXUSEARMOUR);
protector[player][thisprot].visible = FALSE;
} }
else // protector is over worm's own head; caused by ramming
protector[player][thisprot].visible = FALSE;
} else if (thissy >= FIRSTTAIL && thissy <= LASTTAIL)
{ if (player == thissy - FIRSTTAIL || worm[player].mode == TONGUE)
protector[player][thisprot].visible = FALSE;
} else if (thissy >= FIRSTPROTECTOR && thissy <= LASTPROTECTOR)
{ protector[player][thisprot].alive = FALSE;
for (which = 0; which <= PROTECTORS; which++)
if (protector[thissy - FIRSTPROTECTOR][which].alive && protector[thissy - FIRSTPROTECTOR][which].x == x && protector[thissy - FIRSTPROTECTOR][which].y == y)
{ protector[thissy - FIRSTPROTECTOR][which].alive = FALSE;
break;
}
draw(x, y, EMPTY);
field[x][y] = EMPTY;
} else if (thissy == STONE || thissy == WOOD)
protector[player][thisprot].visible = FALSE;
else if (thissy >= FIRSTMISSILE && thissy <= LASTMISSILE)
{ if (player != thissy - FIRSTMISSILE)
{ effect(FXUSEPROTECTOR);
stopfx(missile[thissy - FIRSTMISSILE].receipt);
missile[thissy - FIRSTMISSILE].alive = FALSE;
} else protector[player][thisprot].visible = FALSE;
} else if (thissy == TELEPORT)
{ protector[player][thisprot].visible = FALSE;
score += TELPOINT;
} else if (thissy == KILLER)
{ effect(FXUSEPROTECTOR);
effect(FXKILLERDEATH);
which = whichkiller(x, y);
protector[player][thisprot].last = BONUS;
killer[which].alive = FALSE;
score += KILLKILLER;
if (worm[player].bias)
{ worm[player].lives += KILLERBLOOD;
stat(player, LIVESLINE);
} } }
if (thissy >= FIRSTLETTER && thissy <= LASTLETTER)
{ wormletter(player, thissy);
putletter(player);
} else if (thissy <= LASTOBJECT)
{ score += object[thissy].score;
if (thissy != SLAYER && thissy != BOMB && thissy != MISSILE && thissy != NITRO && thissy != POWER && thissy != AMMO)
effect(FXGETOBJECT);
switch(thissy)
{
case BONUS:
if (!worm[player].bias)
thisletter = rand() % (LETTERS + 1);
else
{ complete = TRUE;
for (which = 0; which <= LETTERS; which++)
if (!letters[player][which])
{ complete = FALSE;
do thisletter = rand() % (LETTERS + 1);
while (letters[player][thisletter]);
break;
}
if (complete)
thisletter = rand() % (LETTERS + 1);
}
letters[player][thisletter] = TRUE;
drawletter(player, FIRSTLETTER + thisletter, NORMAL);
break;
case AMMO:
effect(FXGETAMMO);
worm[player].ammo += (rand() % AMMOMAX) + 1;
stat(player, AMMOLINE);
break;
case ARMOUR:
/* FXUSEDRILL sample will automatically stop, because
a square with ARMOUR is not a square with TAIL. */
worm[player].armour += MODEADD + (rand() % MODERAND);
worm[player].mode = ARMOUR;
stat(player, ARMOURLINE);
break;
case TONGUE:
worm[player].tongue += MODEADD + (rand() % MODERAND);
worm[player].mode = TONGUE;
stat(player, TONGUELINE);
worm[player].last = FIRSTTAIL + player;
break;
case NITRO:
effect(FXGETNITRO);
worm[player].nitro = TRUE;
stat(player, SPEEDLINE);
break;
case BOMB:
flag = FALSE;
which = whichtimebomb(x, y);
if (which != -1)
{ flag = TRUE;
if (creature == HEAD)
{ // push timebomb
if (valid(x + worm[player].deltax, y + worm[player].deltay))
{ ithis = field[x + worm[player].deltax][y + worm[player].deltay];
if (ithis == TELEPORT)
score += BOMBOVEREDGE;
else if (ithis == SKULL)
{ effect(FXGETSKULL);
score += SKULLPOINT;
} else if (ithis <= LASTEMPTY)
{ if (ithis <= LASTOBJECT)
{ score += object[thissy].score;
if (ithis == BOMB)
{ iwhich = whichtimebomb(x + worm[player].deltax, y + worm[player].deltay);
if (iwhich != -1)
timebomb[iwhich].alive = FALSE;
} }
timebomb[which].x += worm[player].deltax;
timebomb[which].y += worm[player].deltay;
field[timebomb[which].x][timebomb[which].y] = BOMB;
draw(timebomb[which].x, timebomb[which].y, ZERO + timebomb[which].time);
} else flag = FALSE;
} else score += BOMBOVEREDGE;
} else protector[player][thisprot].visible = FALSE;
}
if (!flag)
{ if (worm[player].mode == NULL)
draw(worm[player].x, worm[player].y, eachworm[player][0][worm[player].deltax + 1 + (worm[player].deltay + 1) * 3]);
else draw(worm[player].x, worm[player].y, eachworm[player][1][worm[player].deltax + 1 + (worm[player].deltay + 1) * 3]);
bombblast(HEAD, player, worm[player].x, worm[player].y);
}
break;
case POWER:
effect(FXGETPOWERUP);
if (worm[player].power < POWERLIMIT)
{ worm[player].power += 2;
stat(player, POWERLINE);
}
break;
case SLAYER:
for (which = 0; which <= ORBS; which++)
if (orb[which].alive)
{ score += orb[which].score;
orb[which].explode = TRUE;
if (worm[player].bias)
worm[player].lives += ORBBLOOD;
}
for (which = 0; which <= KILLERS; which++)
if (killer[which].alive)
{ effect(FXKILLERDEATH);
killer[which].alive = FALSE;
score += KILLKILLER;
if (worm[player].bias)
worm[player].lives += KILLERBLOOD;
field[killer[which].x][killer[which].y] = BONUS;
draw(killer[which].x, killer[which].y, BONUS);
}
for (which = lo; which <= hi; which++)
if (player != which && worm[which].mode != ARMOUR)
{ worm[which].alive = FALSE;
worm[which].cause = SLAYER;
worm[which].victor = player;
}
for (x = 0; x <= FIELDX; x++)
for (y = 0; y <= FIELDY; y++)
if (field[x][y] == SLIME)
{ draw(x, y, EMPTY);
field[x][y] = EMPTY;
}
if (worm[player].bias)
stat(player, LIVESLINE);
break;
case PROTECTOR:
done = FALSE;
for (which = 0; which <= PROTECTORS; which++)
if (!protector[player][which].alive && !done)
{ do
{ protector[player][which].relx = ((rand() % 2) * 2) - 1;
protector[player][which].rely = ((rand() % 2) * 2) - 1;
for (iwhich = 0; iwhich <= PROTECTORS; iwhich++)
if (which == NOSE || !protector[player][iwhich].alive || protector[player][iwhich].x != xwrap(worm[player].x + protector[player][which].relx) || protector[player][iwhich].y != ywrap(worm[player].y + protector[player][which].rely))
{ effect(FXPROTECTORBORN);
done = TRUE;
protector[player][which].alive = TRUE;
protector[player][which].visible = FALSE;
protector[player][which].last = EMPTY;
if (which == NOSE)
worm[player].position = -1;
}
} while (!done);
}
break;
case MISSILE:
if (!missile[player].alive)
{ missile[player].receipt = effect(FXMISSILEACTIVE);
missile[player].alive = TRUE;
missile[player].x = worm[player].x;
missile[player].y = worm[player].y;
missile[player].moved = FALSE;
}
break;
case LIFE:
worm[player].lives += (rand() % LIFEMAX) + 1;
stat(player, LIVESLINE);
break;
case MULTIPLIER:
if (worm[player].multi < MULTILIMIT)
worm[player].multi *= 2;
break;
case BIAS:
worm[player].bias += MODEADD + (rand() % MODERAND);
stat(player, BIASLINE);
break;
case ICE:
worm[player].ice += ICEADD + (rand() % ICERAND);
ice = player;
break;
case GROWER:
effect(FXGETGROWER);
for (x = 0; x <= FIELDX; x++)
for (y = 0; y <= FIELDY; y++)
if (field[x][y] == GOLD)
{ for (xx = x - 1; xx <= x + 1; xx++)
for (yy = y - 1; yy <= y + 1; yy++)
if (valid(xx, yy))
if (field[xx][yy] == EMPTY || field[xx][yy] == TEMPSILVER)
field[xx][yy] = TEMPGOLD;
else if (field[xx][yy] == SILVER)
field[xx][yy] = WASSILVER;
}
else if (field[x][y] == SILVER || field[x][y] == WASSILVER)
for (xx = x - 1; xx <= x + 1; xx++)
for (yy = y - 1; yy <= y + 1; yy++)
if (valid(xx, yy) && field[xx][yy] == EMPTY)
field[xx][yy] = TEMPSILVER;
for (x = 0; x <= FIELDX; x++)
for (y = 0; y <= FIELDY; y++)
if (field[x][y] == TEMPGOLD || field[x][y] == WASSILVER)
{ field[x][y] = GOLD;
draw(x, y, GOLD);
}
else if (field[x][y] == TEMPSILVER)
{ field[x][y] = SILVER;
draw(x, y, SILVER);
}
break;
case TREASURE:
treasurer = player;
if (level)
{ say((STRPTR) "Treasure!", worm[treasurer].colour);
secondsperlevel = 0;
}
secondsperlevel += TREASUREADD + (rand() % TREASURERAND);
if (level)
{ stat(player, SCORELINE);
reallevel = level;
level = 0;
newlevel(player);
}
break;
case AFFIXER:
worm[player].affixer = TRUE;
break;
case SWITCHER:
if (lo != hi)
for (x = 0; x <= FIELDX; x++)
for (y = 0; y <= FIELDY; y++)
if (field[x][y] >= FIRSTTAIL && field[x][y] <= LASTTAIL && field[x][y] != FIRSTTAIL + player)
{ field[x][y] = FIRSTTAIL + player;
draw(x, y, FIRSTTAIL + player);
}
break;
case HEALER:
if (worm[player].lives < 100)
worm[player].lives = 100;
else worm[player].lives = LIFEMAX;
stat(player, LIVESLINE);
break;
default:
break;
} }
else
{ switch (thissy)
{
case EMPTY:
score += EMPTYPOINT;
break;
case SILVER:
score += SILVERPOINT;
break;
case GOLD:
score += GOLDPOINT;
break;
case ORB:
iwhich = whichorb(x, y);
if (worm[player].mode == ARMOUR || creature == PROTECTOR)
{ if (creature == HEAD)
effect(FXUSEARMOUR);
else effect(FXUSEPROTECTOR);
effect(FXORBDEATH);
score += orb[iwhich].score;
orb[iwhich].alive = FALSE;
if (worm[player].bias)
{ worm[player].lives += ORBBLOOD;
stat(player, LIVESLINE);
} }
else
{ if (orb[iwhich].mode == ARMOUR)
{ effect(FXUSEARMOUR);
orb[iwhich].score += KILLWORM * orb[iwhich].multi;
} else orb[iwhich].alive = FALSE;
worm[player].cause = ORB;
worm[player].victor = -1;
worm[player].alive = FALSE;
}
break;
case FRAGMENT:
whichfrag(x, y, &which, &iwhich);
if (creature == HEAD)
{ if (worm[player].mode != ARMOUR)
{ worm[player].cause = FRAGMENT;
worm[player].victor = -1;
worm[player].alive = FALSE;
frag[which][iwhich].alive = FALSE;
} else
{ effect(FXUSEARMOUR);
reflect(which, iwhich);
} }
else
{ effect(FXUSEPROTECTOR);
reflect(which, iwhich);
}
break;
case SKULL:
effect(FXGETSKULL);
score += SKULLPOINT;
for (which = lo; which <= hi; which++)
{ if (worm[which].alive == FALSE && worm[which].x == worm[player].x && worm[which].y == worm[player].y)
iwhich = which;
}
worm[player].bias += worm[iwhich].bias;
if (worm[player].bias > 0)
{ stat(player, BIASLINE);
worm[iwhich].bias = 0;
stat(iwhich, BIASLINE);
}
worm[player].multi *= worm[iwhich].multi;
if (worm[player].multi > 1)
{ if (worm[player].multi > MULTILIMIT)
worm[player].multi = MULTILIMIT;
}
worm[player].power += worm[iwhich].power;
if (worm[player].power > 1)
{ if (worm[player].power > POWERLIMIT)
worm[player].power = POWERLIMIT;
stat(player, POWERLINE);
worm[iwhich].power = 0;
stat(iwhich, POWERLINE);
}
worm[player].ammo += worm[iwhich].ammo;
if (worm[player].ammo > 0)
{ stat(player, AMMOLINE);
worm[iwhich].ammo = 0;
stat(iwhich, AMMOLINE);
}
worm[player].armour += worm[iwhich].armour;
if (worm[player].armour > 0)
{ stat(player, ARMOURLINE);
worm[iwhich].armour = 0;
stat(iwhich, ARMOURLINE);
}
worm[player].tongue += worm[iwhich].tongue;
if (worm[player].tongue > 0)
{ stat(player, TONGUELINE);
worm[iwhich].tongue = 0;
stat(iwhich, TONGUELINE);
}
if (worm[player].armour > 0 || worm[player].tongue > 0)
{ if (worm[player].armour >= worm[player].tongue)
worm[player].mode = ARMOUR;
else worm[player].mode = TONGUE;
}
if (worm[iwhich].nitro)
{ worm[player].nitro = TRUE;
stat(player, SPEEDLINE);
worm[iwhich].nitro = FALSE;
worm[iwhich].speed = NORMAL;
stat(iwhich, SPEEDLINE);
}
for (which = 0; which <= LETTERS; which++)
if (letters[iwhich][which])
{ blitmode(BLACK);
drawletter(iwhich, FIRSTLETTER + which, BLACK);
blitmode(NORMAL);
if (!letters[player][which])
{ letters[player][which] = TRUE;
drawletter(player, FIRSTLETTER + which, NORMAL);
} }
break;
default:
break;
} }
if (creature == HEAD)
// it is important that HEAD is done before PROTECTORs
{ if (worm[player].tonguereceipt && (thissy < FIRSTTAIL || thissy > LASTTAIL))
{ stopfx(worm[player].tonguereceipt);
worm[player].tonguereceipt = 0L;
}
field[worm[player].x][worm[player].y] = FIRSTHEAD + player;
if (worm[player].alive)
{ switch (worm[player].mode)
{
case NULL:
draw(worm[player].x, worm[player].y, eachworm[player][0][worm[player].deltax + 1 + (worm[player].deltay + 1) * 3]);
break;
case TONGUE:
if (worm[player].tongue < 10 && (r / VERYSLOW) % 2 == 0)
blitmode(WHITE);
else
blitmode(NORMAL);
draw(worm[player].x, worm[player].y, eachworm[player][1][worm[player].deltax + 1 + (worm[player].deltay + 1) * 3]);
blitmode(NORMAL);
break;
case ARMOUR:
if (worm[player].armour < 10 && (r / VERYSLOW) % 2 == 0)
blitmode(WHITE);
else
blitmode(NORMAL);
draw(worm[player].x, worm[player].y, eachworm[player][1][worm[player].deltax + 1 + (worm[player].deltay + 1) * 3]);
blitmode(NORMAL);
break;
default:
break;
} }
else draw(worm[player].x, worm[player].y, SKULL);
} else // assumes creature == PROTECTOR
if (protector[player][thisprot].alive && protector[player][thisprot].visible)
{ field[x][y] = FIRSTPROTECTOR + player;
draw(x, y, FIRSTPROTECTOR + player);
}
while (++thisprot <= PROTECTORS)
if (protector[player][thisprot].alive && valid(protector[player][thisprot].x, protector[player][thisprot].y))
break;
if (thisprot > PROTECTORS)
creature = NULL;
else creature = PROTECTOR;
}
wormscore(player, score);
}
void wormscore(SBYTE player, LONG score)
{ worm[player].score += score * worm[player].multi;
stat(player, SCORELINE);
}
SWORD wsign(SWORD value)
{ if (value < 0)
return (-1);
else if (value > 0)
return (1);
else
return (0);
}
SWORD xpixeltosquare(SWORD x)
{ x = (x - STARTXPIXEL) / SQUAREX;
if (x < 0)
x--;
return (x);
}
SWORD ypixeltosquare(SWORD y)
{ y = (y - STARTYPIXEL) / SQUAREY;
if (y < 0)
y--;
return (y);
}
SBYTE xwrap(SBYTE x)
{ if (x < 0)
x += FIELDX + 1;
else if (x > FIELDX)
x -= FIELDX + 1;
return(x);
}
SBYTE ywrap(SBYTE y)
{ if (y < 0)
y += FIELDY + 1;
else if (y > FIELDY)
y -= FIELDY + 1;
return(y);
}
// Must have blank line at EOF.